20 using System.IO.Compression;
23 namespace Deveel.Data.Store {
25 private const int Magic = 0x012BC53A9;
27 private const int DeletedFlag = 0x020000;
28 private const int CompressedFlag = 0x010;
30 private const int PageSize = 64;
39 throw new ArgumentOutOfRangeException(
"id");
41 throw new ArgumentNullException(
"store");
51 if (firstDeleteChainRecord == -1) {
53 fixedList.IncreaseSize();
55 int newBlockNumber = fixedList.BlockCount - 1;
56 long startIndex = fixedList.BlockFirstPosition(newBlockNumber);
57 long sizeOfBlock = fixedList.BlockNodeCount(newBlockNumber);
60 IArea a = fixedList.GetRecord(startIndex);
66 a.WriteInt8(recordOffset);
68 for (
long n = 1; n < sizeOfBlock - 1; ++n) {
69 a.WriteInt4(DeletedFlag);
72 a.WriteInt8(startIndex + n + 1);
75 a.WriteInt4(DeletedFlag);
83 firstDeleteChainRecord = startIndex + 1;
84 fixedList.WriteDeleteHead(firstDeleteChainRecord);
92 long recycledRecord = firstDeleteChainRecord;
93 IArea block = fixedList.GetRecord(recycledRecord);
96 int status = block.ReadInt4();
97 if ((status & DeletedFlag) == 0)
98 throw new InvalidOperationException(
"Assertion failed: record is not deleted!");
107 long nextChain = block.ReadInt8();
108 firstDeleteChainRecord = nextChain;
111 fixedList.WriteDeleteHead(firstDeleteChainRecord);
119 block.WriteInt8(recordOffset);
124 return recycledRecord;
133 long fixedListOffset = fixedList.Create();
136 firstDeleteChainRecord = -1;
137 fixedList.WriteDeleteHead(-1);
141 IArea blobStoreHeader = store.CreateArea(32);
142 long blobStoreId = blobStoreHeader.
Id;
144 blobStoreHeader.WriteInt4(Magic);
145 blobStoreHeader.WriteInt4(1);
146 blobStoreHeader.WriteInt8(fixedListOffset);
147 blobStoreHeader.
Flush();
153 public void Open(
long offset) {
155 IArea headerArea = store.GetArea(offset);
159 int magic = headerArea.ReadInt4();
160 int version = headerArea.ReadInt4();
162 throw new IOException(
"The magic value for this Object Store is not correct.");
164 throw new IOException(
"The version number for this Object Store is not correct.");
166 long fixedListOffset = headerArea.ReadInt8();
167 fixedList.Open(fixedListOffset);
170 firstDeleteChainRecord = fixedList.ReadDeleteHead();
176 public int Id {
get;
private set; }
180 throw new IOException(
"Negative object size not allowed.");
186 long pageCount = ((maxSize - 1) / (PageSize * 1024)) + 1;
187 IArea objArea = store.CreateArea((pageCount * 8) + 32);
188 long objAreaId = objArea.
Id;
192 type |= CompressedFlag;
195 objArea.WriteInt4(0);
196 objArea.WriteInt4(type);
197 objArea.WriteInt8(maxSize);
198 objArea.WriteInt8(0);
199 objArea.WriteInt8(pageCount);
202 for (
long i = 0; i < pageCount; ++i) {
203 objArea.WriteInt8(-1);
210 long refId = AddToRecordList(objAreaId);
211 return new LargeObject(
this, refId, maxSize, 0, compressed,
false);
223 CurrentSize = currentSize;
225 IsCompressed = compressed;
226 IsComplete = isComplete;
234 public long RawSize {
get;
private set; }
236 public long CurrentSize {
get;
private set; }
238 public bool IsCompressed {
get;
private set; }
240 public bool IsComplete {
get;
private set; }
242 public int Read(
long offset, byte[] buffer,
int length) {
243 return store.ReadObjectPart(Id.Id, offset, buffer, 0, length);
246 public void Write(
long offset, byte[] buffer,
int length) {
248 throw new IOException(
"The object is complete and cannot be written.");
250 CurrentSize += length;
251 store.WriteObjectPart(Id.Id, offset, buffer, 0, length);
255 store.CompleteObject(
this);
259 store.EstablishReference(Id.Id);
263 return store.ReleaseReference(Id.Id);
273 long refId = obj.
Id.
Id;
277 IArea block = fixedList.GetRecord(refId);
282 int status = block.ReadInt4();
285 throw new IOException(
"Assertion failed: record is not open.");
287 int refCount = block.ReadInt4();
288 long size = block.ReadInt8();
289 long currentSize = block.ReadInt8();
290 long pageCount = block.ReadInt8();
300 block.WriteInt8(pageCount);
311 private void WriteObjectPart(
long id,
long objOffset, byte[] buffer,
int off,
int length) {
313 if (objOffset%(PageSize*1024) != 0)
314 throw new Exception(
"Assert failed: offset is not 64k aligned.");
317 if (length > (PageSize * 1024)) {
318 throw new Exception(
"Assert failed: length is greater than 64K.");
327 if (id < 0 || id >= fixedList.NodeCount)
328 throw new IOException(
"Object id is out of range.");
330 IArea block = fixedList.GetRecord(
id);
331 var status = block.ReadInt4();
332 if ((status & DeletedFlag) != 0)
333 throw new InvalidOperationException(
"Assertion failed: record is deleted!");
336 maxSize = block.ReadInt8();
337 currentSize = block.ReadInt8();
338 objPos = block.ReadInt8();
342 IArea area = store.GetArea(objPos);
344 var type = area.ReadInt4();
345 var size = area.ReadInt8();
348 if (objOffset < 0 || objOffset + length > size) {
349 throw new IOException(
"Object invalid write. offset = " + objOffset +
", length = " + length +
", size = " + size);
353 long pageNumber = (objOffset / (PageSize * 1024));
354 area.
Position = (int)((pageNumber * 8) + 32);
355 long pagePos = area.ReadInt8();
360 throw new Exception(
"Assert failed: page position is not -1");
366 if ((type & CompressedFlag) != 0) {
369 var deflateStream =
new DeflateStream(
new MemoryStream(buffer, off, length), CompressionMode.Compress,
false);
370 toWrite =
new byte[PageSize * 1024];
371 writeLength = deflateStream.Read(toWrite, 0, toWrite.Length);
373 throw new NotSupportedException(
"Compression not supported in PCL.");
378 writeLength = length;
385 IArea pageArea = store.CreateArea(writeLength + 8);
386 pagePos = pageArea.
Id;
387 pageArea.WriteInt4(1);
388 pageArea.WriteInt4(writeLength);
389 pageArea.
Write(toWrite, 0, writeLength);
394 area.
Position = (int)((pageNumber * 8) + 24);
395 area.WriteInt8(currentSize + writeLength);
396 area.WriteInt8(pagePos);
404 private int ReadObjectPart(
long id,
long objOffset, byte[] buffer,
int off,
int length) {
406 if (off % (64 * 1024) != 0) {
407 throw new Exception(
"Assert failed: offset is not 64k aligned.");
410 if (length > (64 * 1024)) {
411 throw new Exception(
"Assert failed: length is greater than 64K.");
421 if (id < 0 || id >= fixedList.NodeCount) {
422 throw new IOException(
"Object ID is out of range.");
426 IArea block = fixedList.GetRecord(
id);
428 status = block.ReadInt4();
430 if ((status & DeletedFlag) != 0)
431 throw new InvalidOperationException(
"Assertion failed: record is deleted!");
436 maxSize = block.ReadInt8();
438 currentSize = block.ReadInt8();
440 objPointer = block.ReadInt8();
445 if (off < 0 || objOffset + length > maxSize) {
446 throw new IOException(
"Invalid Read. offset = " + objOffset +
", length = " + length +
" > maxSize = " + maxSize);
450 IArea area = store.GetArea(objPointer);
452 int type = area.ReadInt4();
455 long pageNumber = (objOffset / (64 * 1024));
456 area.
Position = (int)((pageNumber * 8) + 32);
457 long pagePointer = area.ReadInt8();
460 IArea pageArea = store.GetArea(pagePointer);
462 int pageType = pageArea.ReadInt4();
463 int pageSize = pageArea.ReadInt4();
465 if ((type & CompressedFlag) != 0) {
468 byte[] pageBuf =
new byte[pageSize];
469 int readCount = pageArea.
Read(pageBuf, 0, pageSize);
471 var deflateStream =
new DeflateStream(
new MemoryStream(pageBuf, 0, pageSize), CompressionMode.Decompress,
false);
473 int resultLength = deflateStream.Read(buffer, off, length);
474 if (resultLength != length)
475 throw new Exception(
"Assert failed: decompressed length is incorrect.");
478 }
catch(InvalidDataException e) {
479 throw new IOException(
"ZIP Data Format Error: " + e.Message);
482 throw new NotSupportedException(
"Compression not supported in PCL.");
487 return pageArea.
Read(buffer, off, length);
495 if (
id.StoreId != Id)
500 if (refId < 0 || refId >= fixedList.NodeCount)
504 IArea block = fixedList.GetRecord(refId);
506 int status = block.ReadInt4();
508 if ((status & DeletedFlag) != 0)
509 throw new InvalidOperationException(
"Assertion failed: record is deleted!");
512 int refCount = block.ReadInt4();
514 maxSize = block.ReadInt8();
516 currentSize = block.ReadInt8();
518 objOffset = block.ReadInt8();
521 IArea area = store.GetArea(objOffset);
525 int type = area.ReadInt4();
527 long blockSize = area.ReadInt8();
529 long pageCount = area.ReadInt8();
531 bool compressed = (type & CompressedFlag) != 0;
532 return new LargeObject(
this,
id.Id, maxSize, currentSize, compressed,
true);
539 IArea block = fixedList.GetRecord(
id);
541 int status = block.ReadInt4();
543 throw new Exception(
"Assertion failed: record is not static.");
545 int refCount = block.ReadInt4();
549 block.WriteInt4(refCount + 1);
552 }
catch (IOException e) {
553 throw new Exception(
"IO Error: " + e.Message);
561 IArea block = fixedList.GetRecord(
id);
563 int status = block.ReadInt4();
565 throw new Exception(
"Assertion failed: Record is not static (status = " + status +
")");
567 int refCount = block.ReadInt4();
569 throw new Exception(
"Releasing when IBlob reference counter is at 0.");
571 var objSize = block.ReadInt8();
572 var objPos = block.ReadInt8();
576 if ((refCount - 1) == 0) {
578 IArea area = store.GetArea(objPos);
581 var type = (byte)area.ReadInt4();
582 var totalSize = area.ReadInt8();
583 var pageCount = area.ReadInt8();
586 for (
long i = 0; i < pageCount; ++i) {
587 long pageOffset = area.ReadInt8();
589 store.DeleteArea(pageOffset);
593 store.DeleteArea(objPos);
597 block.WriteInt4(DeletedFlag);
600 block.WriteInt8(firstDeleteChainRecord);
603 firstDeleteChainRecord = id;
606 fixedList.WriteDeleteHead(firstDeleteChainRecord);
613 block.WriteInt4(refCount - 1);
618 }
catch (IOException e) {
619 throw new Exception(
"IO Error: " + e.Message);
ILargeObject CreateNewObject(long maxSize, bool compressed)
Creates a new large object returning a reference to it.
long firstDeleteChainRecord
Defines a referenced object that can be accessed on a multi-phase level.
Defines the contract for stores that handle lrge objects within a database system.
int Read(byte[] buffer, int offset, int length)
Reads an array of bytes from the underlying IArea and advances the position by length ...
ILargeObject GetObject(ObjectId id)
Gets an object that was previously created for the given unique identifier.
long Position
Returns or sets the current position of the pointer within the area.
int ReadObjectPart(long id, long objOffset, byte[] buffer, int off, int length)
long Id
Gets the unique identifier of the object within the containing store.
void Write(byte[] buffer, int offset, int length)
int Read(long offset, byte[] buffer, int length)
Reads the content of the object, starting at a given offset, into the buffer given, for the number of bytes specified.
bool ReleaseReference(long id)
readonly ObjectStore store
An interface for access the contents of an area of a store.
A unique identifier of an object within a database system, that is composed by a reference to the sto...
readonly FixedRecordList fixedList
void WriteObjectPart(long id, long objOffset, byte[] buffer, int off, int length)
void EstablishReference(long id)
void Complete()
Marks the object as complete.
void Write(long offset, byte[] buffer, int length)
Write the given binary content into the object, starting at the given offset for the number of bytes ...
void CompleteObject(LargeObject obj)
LargeObject(ObjectStore store, long refId, long size, long currentSize, bool compressed, bool isComplete)
ObjectStore(int id, IStore store)
long AddToRecordList(long recordOffset)
long Id
Returns the unique identifier that represents this area.
A store is a resource where areas can be allocated and freed to store information (a memory allocator...
void Establish()
Establishes a reference of the object to the underlying store which contains it.
bool Release()
Removes a reference of the object from the underlying store which contains it.