aboutsummaryrefslogtreecommitdiff
path: root/src/stable/myriad.h
blob: 7cf604184a826a6802c1906e6a5052b2a47ecf9c (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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
#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, ///< Start writing at end of file
            //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 );
        Bu::String getLocation() const;

    private:
        bool loadMyriad();
        void createMyriad( int32_t iBlockSize, int32_t iPreallocateBlocks );
        void writeHeader();
        int32_t __calcHeaderSize();
        int32_t allocateBlock();
        int32_t __allocateBlock();
        void releaseBlock( int32_t iBlockId, bool bBlank=true );
        void __releaseBlock( int32_t iBlockId, bool bBlank=true );
        void blankBlock( int32_t iBlockId );

        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 iBlock, int32_t iStart,
                void *pTarget, int32_t iSize );

        /**
         * Block restricted write, it will not write past the end of the block
         * that iStart places it in. If this returns a non-zero number it's an
         * indication that you need to allocate a new block.
         */
        int32_t blockWrite( int32_t iBlock, int32_t iStart,
                const 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 getSize() const;
            int32_t getBlockSize() const;
            StreamId getStreamId() const;
            int32_t getOpenCount() const;

            void setSize( int32_t iNewSize );
            int32_t read( int32_t iStart, void *pTarget, int32_t iSize );
            int32_t write( int32_t iStart, const void *pTarget, int32_t iSize );
            Bu::String getLocation() const;
            Bu::Array<int32_t> getBlockList() const;

            /**
             * 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;
        mutable Bu::Mutex mAccess;
        mutable Bu::Mutex mBacking;
        Bu::Stream &rBacking;
        int32_t iBlockSize;
        int32_t iBlockCount;
        bool bIsNewStream;
        bool bStructureChanged;
        StreamHash hStream;
        IndexList lFreeBlocks;
        StreamId iLastUsedIndex;
    };
};

#endif