aboutsummaryrefslogtreecommitdiff
path: root/src/stable/myriad.h
blob: 07b4a1d783e722031b05662f89fab1959de12dad (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#ifndef BU_MYRIAD_H
#define BU_MYRIAD_H

#include "bu/stream.h"
#include "bu/exceptionbase.h"
#include "bu/mutex.h"
#include "bu/array.h"
#include "bu/hash.h"

namespace Bu
{
    class MyriadStream;

    subExceptionDeclBegin( MyriadException )
        enum
        {
            emptyStream,
            invalidFormat,
            badVersion,
            invalidWordSize,
            noSuchStream,
            streamExists,
            invalidStreamId,
            protectedStream,
            invalidParameter,
            invalidBackingStream,
            badMode,
        };
    subExceptionDeclEnd();

    class Myriad
    {
    public:
        typedef int32_t StreamId;
        enum Mode {
            None        = 0x00,

            // Flags
            Read        = 0x01, ///< Open file for reading
            Write       = 0x02, ///< Open file for writing
            Create      = 0x04, ///< Create file if it doesn't exist
            Truncate    = 0x08, ///< Truncate file if it does exist
            Append      = 0x10, ///< Always append on every write
            NonBlock    = 0x20, ///< Open file in non-blocking mode
            Exclusive   = 0x44, ///< Create file, if it exists then fail

            // Helpful mixes
            ReadWrite   = 0x03, ///< Open for reading and writing
            WriteNew    = 0x0E  ///< Create a file (or truncate) for writing.
                                /// Same as Write|Create|Truncate
        };

    public:
        /**
         * Open existing Myriad stream, or initialize a new one if it doesn't
         * exist.
         *
         * Myriad format V0
         *   0 -   3:  Myriad_MAGIC_CODE (0ad3fa84)
         *   4 -   4:  Version Id (1)
         *   5 -   5:  Bits per integer (32)
         *   6 -   9:  Block size in bytes.
         *  10 -  13:  Number of streams.
         *  14 - ...:  Stream Data
         *
         * Stream Data:
         *   0 -   3:  Stream Id
         *   4 -   7:  Size of stream in bytes
         *   8 - ...:  List of blocks in stream (4 bytes per block
         */
        Myriad( Bu::Stream &rBacking, int32_t iBlockSize=-1, int32_t iPreallocateBlocks=-1 );
        virtual ~Myriad();

        MyriadStream create( Mode eMode, int32_t iPreallocateBytes=-1 );
        MyriadStream open( StreamId iStream, Mode eMode );
        bool erase( StreamId iStream );
        bool setSize( StreamId iStream, int32_t iNewSize );

    private:
        bool loadMyriad();
        void createMyriad( int32_t iBlockSize, int32_t iPreallocateBlocks );
        int32_t allocateBlock();

        void openStream( StreamId id );
        void closeStream( StreamId id );
        /**
         * Block restricted read, it will not read past the end of the block
         * that iStart places it in.
         */
        int32_t blockRead( int32_t iStart, void *pTarget, int32_t iSize );

    public:
        /**
         * Bridge/communication/tracking class for individual Myriad streams.
         * Not for general use, this is used by Myriad and MyriadStream to
         * control access.
         */
        class Stream
        {
        friend Bu::Myriad;
        private:
            Stream( Myriad &rParent, StreamId iStream, int32_t iSize );
            virtual ~Stream();

        public:
            int32_t read( int32_t iStart, void *pTarget, int32_t iSize );

            /**
             * Doesn't actually open, just increments the open counter.
             * If the open counter is non-zero then at least one stream has
             * a lock on this stream.
             */
            void open();

            /**
             * Doesn't actually close, just decrements the open counter.
             *@returns true if there are still handles open, false if no
             * streams have a lock.
             */
            bool close();

        private:
            mutable Bu::Mutex mAccess;
            Myriad &rParent;
            StreamId iStream;
            int32_t iSize;
            Bu::Array<int32_t> aBlocks;
            int32_t iOpenCount;
            bool bStructureChanged;
        };

    private:

        typedef Bu::Hash<StreamId, Stream *> StreamHash;

        typedef Bu::List<int32_t> IndexList;
        Bu::Mutex mAccess;
        mutable Bu::Mutex mBacking;
        Bu::Stream &rBacking;
        int32_t iBlockSize;
        int32_t iBlockCount;
        bool bIsNewStream;
        StreamHash hStream;
        IndexList lFreeBlocks;
    };
};

#endif