aboutsummaryrefslogtreecommitdiff
path: root/src/stable/myriad.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/stable/myriad.h347
1 files changed, 181 insertions, 166 deletions
diff --git a/src/stable/myriad.h b/src/stable/myriad.h
index 14467a4..5accd1e 100644
--- a/src/stable/myriad.h
+++ b/src/stable/myriad.h
@@ -1,24 +1,16 @@
1/*
2 * Copyright (C) 2007-2023 Xagasoft, All rights reserved.
3 *
4 * This file is part of the libbu++ library and is released under the
5 * terms of the license contained in the file LICENSE.
6 */
7
8#ifndef BU_MYRIAD_H 1#ifndef BU_MYRIAD_H
9#define BU_MYRIAD_H 2#define BU_MYRIAD_H
10 3
11#include <stdint.h> 4#include "bu/stream.h"
12#include "bu/bitstring.h"
13#include "bu/exceptionbase.h" 5#include "bu/exceptionbase.h"
6#include "bu/mutex.h"
14#include "bu/array.h" 7#include "bu/array.h"
15#include "bu/hash.h" 8#include "bu/hash.h"
16#include "bu/mutex.h" 9
17#include "bu/extratypes.h" 10#include "bu/bitstring.h"
18 11
19namespace Bu 12namespace Bu
20{ 13{
21 class Stream;
22 class MyriadStream; 14 class MyriadStream;
23 15
24 subExceptionDeclBegin( MyriadException ) 16 subExceptionDeclBegin( MyriadException )
@@ -31,206 +23,229 @@ namespace Bu
31 noSuchStream, 23 noSuchStream,
32 streamExists, 24 streamExists,
33 invalidStreamId, 25 invalidStreamId,
34 protectedStream 26 protectedStream,
27 invalidParameter,
28 invalidBackingStream,
29 badMode,
30 streamOpen,
35 }; 31 };
36 subExceptionDeclEnd(); 32 subExceptionDeclEnd();
37 33
38 /** 34 /**
39 * Myriad block-allocated stream multiplexing system. This is a system for 35 * Myriad Stream Multiplexer. This is a system that allows you to store
40 * creating streams that contain other streams in a flexible and lightweight 36 * many streams within a single backing stream. This is great for databases,
41 * manner. Basically, you can create a file (or any other stream) that can 37 * caching, etc. It's fairly lightweight, and allows all streams to grow
42 * store any number of flexible, growing streams. The streams within the 38 * dynamically using a block-allocation scheme. This is used extensively
43 * Myriad stream are automatically numbered, not named. This works more 39 * by the caching system and MyriadFs as well as other systems within
44 * or less like a filesystem, but without the extra layer for managing 40 * libbu++.
45 * file and directory links. This would actually be very easy to add
46 * on top of Myriad, but is not required.
47 *
48 * Header format is as follows:
49 *
50 * MMMMvBssssSSSS*
51 * M = Magic number (0AD3FA84)
52 * v = version number
53 * B = Bits per int
54 * s = Blocksize in bytes
55 * S = Number of Streams
56 *
57 * The * represents the Stream headers, one per stream, as follows:
58 * IIIIssss$
59 * I = Id number of the stream
60 * s = size of stream in bytes
61 *
62 * The $ represents the Block headers, one per used block, as follows:
63 * IIII
64 * I = Index of the block
65 *
66 * The stream/block data is interleaved in the header, so all blocks stored
67 * with one stream are together. The block headers are in order, and the
68 * data in them is required to be "solid" you cannot fill partial blocks
69 * mid-way through a stream.
70 *
71 * The initial block starts with the nids header, and is both the zero block
72 * and the zero stream. For now, the minimum block size is the size needed
73 * to store the base header, the zero stream header, and the first two
74 * blocks of the zero stream, so 30 bytes. Since it's reccomended to use
75 * a size that will fit evenly into filesystem blocks, then a size of 32 is
76 * probably the smallest reccomended size because all powers of two equal
77 * to or greater than 32 are evenly divisible by 32.
78 *
79 * I have had a thought that if the block size were smaller than 42 bytes
80 * the header would consume the first N blocks where N * block size is
81 * enough space to house the initial header, the first stream header, and
82 * the first N block headers. This, of course, causes you to hit an
83 * infinite header if the block size is small enough.
84 */ 41 */
85 class Myriad 42 class Myriad
86 { 43 {
87 friend class MyriadStream; 44 public:
45 typedef int32_t StreamId;
46 typedef Bu::Array<StreamId> StreamIdArray;
47 typedef Bu::List<StreamId> StreamIdList;
48 enum Mode : int32_t {
49 None = 0x00,
50
51 // Flags
52 Read = 0x01, ///< Open file for reading
53 Write = 0x02, ///< Open file for writing
54 Create = 0x04, ///< Create file if it doesn't exist
55 Truncate = 0x08, ///< Truncate file if it does exist
56 Append = 0x10, ///< Start writing at end of file
57 //NonBlock = 0x20, ///< Open file in non-blocking mode
58 Exclusive = 0x40, ///< Create file, if it exists then fail
59
60 // Helpful mixes
61 ReadWrite = 0x03, ///< Open for reading and writing
62 WriteNew = 0x0E ///< Create a file (or truncate) for writing.
63 /// Same as Write|Create|Truncate
64 };
65
88 public: 66 public:
89 /** 67 /**
90 * Create a Myriad object that uses the given stream to store data. 68 * Open existing Myriad container, or initialize a new one if the
91 * This stream must be random access. The block size and preallocate 69 * backing stream is empty. If other data is already in the provided
92 * values passed in are values that will be used if the given stream 70 * backing stream an error is thrown.
93 * is empty. In that case the stream will be "formatted" for myriad 71 *
94 * with the specified block size. If there is already a viable Myriad 72 * Myriad format V0
95 * format present in the stream, then the blocksize and preallocate 73 * 0 - 3: Myriad_MAGIC_CODE (0ad3fa84)
96 * values will be ignored and the values from the stream will be used 74 * 4 - 4: Version Id (1)
97 * instead. If the stream doesn't appear to be Myriad formatted an 75 * 5 - 5: Bits per integer (32)
98 * exception will be thrown. 76 * 6 - 9: Block size in bytes.
77 * 10 - 13: Number of streams.
78 * 14 - ...: Stream Data
79 *
80 * Stream Data:
81 * 0 - 3: Stream Id
82 * 4 - 7: Size of stream in bytes
83 * 8 - ...: List of blocks in stream (4 bytes per block
99 */ 84 */
100 Myriad( Bu::Stream &sStore, int iBlockSize=512, int iPreallocate=8 ); 85 Myriad( Bu::Stream &rBacking, int32_t iBlockSize=-1,
86 int32_t iPreallocateBlocks=-1 );
101 virtual ~Myriad(); 87 virtual ~Myriad();
102 88
103 /** 89 /**
104 * Destroy whatever data may be in the base stream and create a new 90 * Creates a new stream open in the specified eMode and, optionally,
105 * Myriad system there with the given blocksize. Use this with care, 91 * preallocates the specificed amount of space. The stream is zero
106 * it will destroy anything that was already in the stream, and 92 * bytes even if space is preallocated. The open stream is returned,
107 * generally, should not ever have to be used. 93 * ready for use. Use this if you don't care what the id is of the
94 * newly created stream.
108 */ 95 */
109 void initialize( int iBlockSize, int iPreAllocate=1 ); 96 MyriadStream create( Mode eMode, int32_t iPreallocateBytes=-1 );
110 97
111 /** 98 /**
112 * Create a new stream within the Myriad system. The ID of the new 99 * Open an existing stream or create a new stream with the specified
113 * stream is returned. 100 * id (iStream) with the specified eMode. This respects the normal file
101 * modes, see Bu::Myriad::Mode for details.
114 */ 102 */
115 int createStream( int iPreAllocate=1 ); 103 MyriadStream open( StreamId iStream, Mode eMode );
116 104
117 /** 105 /**
118 * Create a new stream within the Myriad system with a given id. The 106 * Allocate a new stream but do not open it, just ensure it exists and
119 * id that you provide will be the new id of the stream unless it's 107 * return the id of the newly allocated stream.
120 * already used, in which case an error is thrown. This is primarilly
121 * useful when copying an old Myriad file into a new one.
122 */ 108 */
123 int createStreamWithId( int iId, int iPreAllocate=1 ); 109 StreamId allocate();
124 110
125 /** 111 /**
126 * Delete a stream that's already within the Myriad. 112 * Erase the stream specified by iStream. This only can work when the
113 * stream is not open at the moment.
127 */ 114 */
128 void deleteStream( int iId ); 115 void erase( StreamId iStream );
129 116 void setSize( StreamId iStream, int32_t iNewSize );
117 int32_t getSize( StreamId iStream ) const;
118 bool exists( StreamId iStream ) const;
119 Bu::String getLocation() const;
120 int32_t getBlockSize() const;
121 int32_t getTotalBlocks() const;
122 int32_t getUsedBlocks() const;
123 int32_t getFreeBlocks() const;
124 int32_t getTotalStreams() const;
125 int32_t getTotalUsedBytes() const;
126 int32_t getTotalUnusedBytes( int32_t iAssumeBlockSize=-1 ) const;
127 Bu::BitString buildBlockUseMap() const;
128 StreamIdArray buildBlockMap() const;
129
130 /** 130 /**
131 * Return a new Stream object assosiated with the given stream ID. 131 * Lists all stream ids that you are allowed to open. Technically there
132 * is always a zero stream, but it is used by Myriad for stream/block
133 * accounting. It works like a normal stream but you should not open
134 * it.
132 */ 135 */
133 MyriadStream openStream( int iId ); 136 StreamIdList getStreamList() const;
134
135 Bu::Array<int> getStreamIds();
136 int getStreamSize( int iId );
137 bool hasStream( int iId );
138
139 int getNumStreams();
140 int getBlockSize();
141 int getNumBlocks();
142 int getNumUsedBlocks();
143 Bu::size getTotalUsedBytes();
144 Bu::size getTotalUnusedBytes();
145 Bu::size getTotalUnusedBytes( int iFakeBlockSize );
146 137
147 /** 138 /**
148 * Syncronize the header data, etc. with the storage stream. It's not 139 * Flush all caches to the backing stream, write all structural and
149 * a bad idea to call this periodically. 140 * header changes.
150 */ 141 */
151 void sync(); 142 void sync();
152 143
144 private:
145 bool loadMyriad();
146 void createMyriad( int32_t iBlockSize, int32_t iPreallocateBlocks );
147 void writeHeader();
148 int32_t __calcHeaderSize();
149 int32_t allocateBlock();
150 int32_t __allocateBlock();
151 void releaseBlock( int32_t iBlockId, bool bBlank=true );
152 void __releaseBlock( int32_t iBlockId, bool bBlank=true );
153 void blankBlock( int32_t iBlockId );
154
155 void openStream( StreamId id );
156 void closeStream( StreamId id );
153 /** 157 /**
154 * Read the first few bytes from the given stream and return true/false 158 * Block restricted read, it will not read past the end of the block
155 * depending on weather or not it's a Myriad stream. This will throw 159 * that iStart places it in.
156 * an exception if the stream is empty, or is not random access.
157 */ 160 */
158 static bool isMyriad( Bu::Stream &sStore, uint8_t &uVer ); 161 int32_t blockRead( int32_t iBlock, int32_t iStart,
159 162 void *pTarget, int32_t iSize );
163
160 /** 164 /**
161 * Read the first few bytes from the given stream and return true/false 165 * Block restricted write, it will not write past the end of the block
162 * depending on weather or not it's a Myriad stream. This will throw 166 * that iStart places it in. If this returns a non-zero number it's an
163 * an exception if the stream is empty, or is not random access. 167 * indication that you need to allocate a new block.
164 */ 168 */
165 static bool isMyriad( Bu::Stream &sStore ); 169 int32_t blockWrite( int32_t iBlock, int32_t iStart,
170 const void *pTarget, int32_t iSize );
166 171
167 const Bu::BitString getBlocksUsed() const; 172 public:
168
169 private:
170 /** 173 /**
171 * Initialize this object based on the data already in the assosiated 174 * Bridge/communication/tracking class for individual Myriad streams.
172 * stream. This will be called automatically for you if you forget, 175 * Not for general use, this is used by Myriad and MyriadStream to
173 * but if you want to pre-initialize for some reason, just call this 176 * control access.
174 * once before you actually start doing anything with your Myriad.
175 */ 177 */
176 void initialize();
177
178 enum
179 {
180 blockUnused = 0xFFFFFFFFUL
181 };
182
183 typedef Bu::Array<int> BlockArray;
184 class Stream 178 class Stream
185 { 179 {
186 public: 180 friend Bu::Myriad;
187 int iId; 181 private:
188 int iSize; 182 Stream( Myriad &rParent, StreamId iStream, int32_t iSize );
189 BlockArray aBlocks; 183 virtual ~Stream();
190 };
191 typedef Bu::Array<Stream *> StreamArray;
192 184
193 class Block
194 {
195 public: 185 public:
196 char *pData; 186 int32_t getSize() const;
197 bool bChanged; 187 int32_t getBlockSize() const;
198 int iBlockIndex; 188 StreamId getStreamId() const;
189 int32_t getOpenCount() const;
190
191 void setSize( int32_t iNewSize );
192 int32_t read( int32_t iStart, void *pTarget, int32_t iSize );
193 int32_t write( int32_t iStart, const void *pTarget, int32_t iSize );
194 Bu::String getLocation() const;
195 Bu::Array<int32_t> getBlockList() const;
196
197 /**
198 * Doesn't actually open, just increments the open counter.
199 * If the open counter is non-zero then at least one stream has
200 * a lock on this stream.
201 */
202 void open();
203
204 /**
205 * Doesn't actually close, just decrements the open counter.
206 *@returns true if there are still handles open, false if no
207 * streams have a lock.
208 */
209 bool close();
210
211 private:
212 mutable Bu::Mutex mAccess;
213 Myriad &rParent;
214 StreamId iStream;
215 int32_t iSize;
216 Bu::Array<int32_t> aBlocks;
217 int32_t iOpenCount;
199 }; 218 };
200 219
201 void updateHeader();
202 int findEmptyBlock();
203
204 /**
205 *@todo Change this to use a binary search, it's nicer.
206 */
207 Stream *findStream( int iId );
208
209 Block *getBlock( int iBlock );
210 void releaseBlock( Block *pBlock );
211 void syncBlock( Block *pBlock );
212
213 int streamAddBlock( Stream *pStream );
214 void setStreamSize( Stream *pStream, long iSize );
215
216 void headerChanged();
217
218 private: 220 private:
219 Bu::Stream &sStore; 221 typedef Bu::Hash<StreamId, Stream *> StreamHash;
220 int iBlockSize; 222 typedef Bu::List<int32_t> IndexList;
221 int iBlocks; 223 mutable Bu::Mutex mAccess;
222 int iUsed; 224 mutable Bu::Mutex mBacking;
223 typedef Bu::List<int> IndexList; 225 Bu::Stream &rBacking;
226 int32_t iBlockSize;
227 int32_t iBlockCount;
228 bool bIsNewStream;
229 bool bStructureChanged;
230 mutable Bu::Mutex mhStream;
231 StreamHash hStream;
224 IndexList lFreeBlocks; 232 IndexList lFreeBlocks;
225// Bu::BitString bsBlockUsed; 233 StreamId iLastUsedIndex;
226 StreamArray aStreams;
227 typedef Bu::Hash<int, Block *> BlockHash;
228 BlockHash hActiveBlocks;
229 bool bHeaderChanged;
230
231 Bu::Mutex mHeader;
232 Bu::Mutex mActiveBlocks;
233 }; 234 };
235 constexpr Myriad::Mode operator&( Myriad::Mode a, Myriad::Mode b )
236 {
237 return static_cast<Myriad::Mode>(
238 static_cast<std::underlying_type<Myriad::Mode>::type>(a) &
239 static_cast<std::underlying_type<Myriad::Mode>::type>(b)
240 );
241 }
242 constexpr Myriad::Mode operator|( Myriad::Mode a, Myriad::Mode b )
243 {
244 return static_cast<Myriad::Mode>(
245 static_cast<std::underlying_type<Myriad::Mode>::type>(a) |
246 static_cast<std::underlying_type<Myriad::Mode>::type>(b)
247 );
248 }
234}; 249};
235 250
236#endif 251#endif