public override unsafe void Write(Slice changeVector, Stream stream, byte[] tempBuffer, OutgoingReplicationStatsScope stats) { fixed(byte *pTemp = tempBuffer) { if (AssertChangeVectorSize() > tempBuffer.Length) { ThrowTooManyChangeVectorEntries(this, Key.ToString()); } var tempBufferPos = WriteCommon(changeVector, pTemp); *(int *)(pTemp + tempBufferPos) = Key.Size; tempBufferPos += sizeof(int); Memory.Copy(pTemp + tempBufferPos, Key.Content.Ptr, Key.Size); tempBufferPos += Key.Size; *(int *)(pTemp + tempBufferPos) = Name.Size; tempBufferPos += sizeof(int); Memory.Copy(pTemp + tempBufferPos, Name.Buffer, Name.Size); tempBufferPos += Name.Size; *(int *)(pTemp + tempBufferPos) = ContentType.Size; tempBufferPos += sizeof(int); Memory.Copy(pTemp + tempBufferPos, ContentType.Buffer, ContentType.Size); tempBufferPos += ContentType.Size; pTemp[tempBufferPos++] = (byte)Base64Hash.Size; Base64Hash.CopyTo(pTemp + tempBufferPos); tempBufferPos += Base64Hash.Size; stream.Write(tempBuffer, 0, tempBufferPos); } }
/// <summary>Creates a new binary buffer, initialized by copying pre-existing data</summary> /// <param name="prefix">Data that will be copied at the start of the buffer</param> /// <param name="capacity">Optional initial capacity of the buffer</param> /// <remarks>The cursor will already be placed at the end of the prefix</remarks> public SliceWriter(Slice prefix, int capacity = 0) { if (capacity < 0) { throw new ArgumentException("Capacity must be a positive integer.", "capacity"); } int n = prefix.Count; Contract.Assert(n >= 0); if (capacity == 0) { // most frequent usage is to add a packed integer at the end of a prefix capacity = SliceHelpers.Align(n + 8); } else { capacity = Math.Max(capacity, n); } var buffer = new byte[capacity]; if (n > 0) { prefix.CopyTo(buffer, 0); } this.Buffer = buffer; this.Position = n; }
internal Slice Grab(Slice slice) { if (slice.IsNullOrEmpty) { return(slice.IsNull ? Slice.Nil : Slice.Empty); } lock (m_lock) { if (slice.Count > m_buffer.Length - m_offset) { // not enough ? if (slice.Count >= 2048) { return(slice.Memoize()); } m_buffer = new byte[4096]; m_offset = 0; } int start = m_offset; slice.CopyTo(m_buffer, m_offset); m_offset += slice.Count; return(m_buffer.AsSlice(start, slice.Count)); } }
private void AssertSliceDataEqual(byte[] expected, Slice actual) { var actualSliceData = new byte[actual.Length]; actual.CopyTo(new ArraySegment <byte>(actualSliceData)); CollectionAssert.AreEqual(expected, actualSliceData); }
/// <summary> /// Internal method that is used when splitting pages /// No need to do any work here, we are always adding at the end /// </summary> internal void CopyNodeDataToEndOfPage(TreeNodeHeader *other, Slice key) { var index = NumberOfEntries; Debug.Assert(HasSpaceFor(TreeSizeOf.NodeEntryWithAnotherKey(other, key) + Constants.Tree.NodeOffsetSize)); var nodeSize = TreeSizeOf.NodeEntryWithAnotherKey(other, key); Debug.Assert(IsBranch == false || index != 0 || key.Size == 0);// branch page's first item must be the implicit ref var newNode = AllocateNewNode(index, nodeSize); Debug.Assert(key.Size <= ushort.MaxValue); newNode->KeySize = (ushort)key.Size; newNode->Flags = other->Flags; if (key.Options == SliceOptions.Key && key.Size > 0) { key.CopyTo((byte *)newNode + Constants.Tree.NodeHeaderSize); } if (IsBranch || other->Flags == (TreeNodeFlags.PageRef)) { newNode->PageNumber = other->PageNumber; newNode->Flags = TreeNodeFlags.PageRef; return; } newNode->DataSize = other->DataSize; Memory.Copy((byte *)newNode + Constants.Tree.NodeHeaderSize + key.Size, (byte *)other + Constants.Tree.NodeHeaderSize + other->KeySize, other->DataSize); }
private void WritePrefix(Slice prefix, int prefixId) { var prefixNodeSize = Constants.PrefixNodeHeaderSize + prefix.Size; prefixNodeSize += prefixNodeSize & 1; var prefixNodeOffset = (ushort)(Upper - prefixNodeSize); Upper = prefixNodeOffset; Debug.Assert(_prefixSection->NextPrefixId == prefixId); if (_prefixSection->PrefixOffsets[prefixId] != 0) { throw new InvalidOperationException(string.Format("Cannot write a prefix '{0}' at the following offset position: {1} because it's already taken by another prefix. The offset for the prefix {1} is {2}. ", prefix, prefixId, _prefixSection->PrefixOffsets[prefixId])); } _prefixSection->PrefixOffsets[prefixId] = prefixNodeOffset; var prefixNodeHeader = (PrefixNodeHeader *)(_base + prefixNodeOffset); prefixNodeHeader->PrefixLength = prefix.Size; prefix.CopyTo((byte *)prefixNodeHeader + Constants.PrefixNodeHeaderSize); _prefixSection->NextPrefixId++; }
private TreeNodeHeader *CreateNode(int index, Slice key, TreeNodeFlags flags, int len) { Debug.Assert(index <= NumberOfEntries && index >= 0); Debug.Assert(IsBranch == false || index != 0 || key.Size == 0);// branch page's first item must be the implicit ref if (HasSpaceFor(key, len) == false) { throw new InvalidOperationException(string.Format("The page is full and cannot add an entry, this is probably a bug. Key: {0}, data length: {1}, size left: {2}", key, len, SizeLeft)); } // move higher pointers up one slot ushort *offsets = KeysOffsets; for (int i = NumberOfEntries; i > index; i--) { offsets[i] = offsets[i - 1]; } var nodeSize = TreeSizeOf.NodeEntry(PageMaxSpace, key, len); var node = AllocateNewNode(index, nodeSize); node->Flags = flags; Debug.Assert(key.Size <= ushort.MaxValue); node->KeySize = (ushort)key.Size; if (key.Options == SliceOptions.Key && node->KeySize > 0) { key.CopyTo((byte *)node + Constants.Tree.NodeHeaderSize); } return(node); }
private NodeHeader *CreateNode(int index, Slice key, NodeFlags flags, int len, ushort previousNodeVersion) { Debug.Assert(index <= NumberOfEntries && index >= 0); Debug.Assert(IsBranch == false || index != 0 || key.Size == 0);// branch page's first item must be the implicit ref if (HasSpaceFor(key, len) == false) { throw new InvalidOperationException("The page is full and cannot add an entry, this is probably a bug"); } // move higher pointers up one slot for (int i = NumberOfEntries; i > index; i--) { KeysOffsets[i] = KeysOffsets[i - 1]; } var nodeSize = SizeOf.NodeEntry(PageMaxSpace, key, len); var node = AllocateNewNode(index, key, nodeSize, previousNodeVersion); if (key.Options == SliceOptions.Key) { key.CopyTo((byte *)node + Constants.NodeHeaderSize); } node->Flags = flags; return(node); }
public void IncreaseSize(int additionalCapacity) { if (additionalCapacity <= 0) { return; } // Creating a new reservation may overwrite the particles this slice is using, so we have to // make a temporary copy of all of our current particles. var temp = new Particle[Slice.Length]; Slice.CopyTo(temp); _pool._allocations.Remove(this); // This is ugly but I'm not in the mood to refactor the reservation logic atm var newAllocation = _pool.Reserve(temp.Length + additionalCapacity); _pool._allocations.Remove(newAllocation); StartIndex = newAllocation.StartIndex; LastUsedIndex = newAllocation.LastUsedIndex; Slice = newAllocation.Slice; temp.CopyTo(Slice); _pool._allocations.Add(this); }
/// <summary> /// Internal method that is used when splitting pages /// No need to do any work here, we are always adding at the end /// </summary> internal void CopyNodeDataToEndOfPage(NodeHeader* other, Slice key = null) { Debug.Assert(SizeOf.NodeEntry(other) + Constants.NodeOffsetSize <= SizeLeft); var index = NumberOfEntries; var nodeSize = SizeOf.NodeEntry(other); key = key ?? new Slice(other); Debug.Assert(IsBranch == false || index != 0 || key.Size == 0);// branch page's first item must be the implicit ref var newNode = AllocateNewNode(index, key, nodeSize); newNode->Flags = other->Flags; key.CopyTo((byte*)newNode + Constants.NodeHeaderSize); if (IsBranch || other->Flags==(NodeFlags.PageRef)) { newNode->PageNumber = other->PageNumber; newNode->Flags = NodeFlags.PageRef; return; } newNode->DataSize = other->DataSize; NativeMethods.memcpy((byte*)newNode + Constants.NodeHeaderSize + other->KeySize, (byte*)other + Constants.NodeHeaderSize + other->KeySize, other->DataSize); }
public byte* AddNode(int index, Slice key, int len, long pageNumber) { Debug.Assert(index <= NumberOfEntries && index >= 0); Debug.Assert(IsBranch == false || index != 0 || key.Size == 0);// branch page's first item must be the implicit ref if (HasSpaceFor(key, len) == false) throw new InvalidOperationException("The page is full and cannot add an entry, this is probably a bug"); // move higher pointers up one slot for (int i = NumberOfEntries; i > index; i--) { KeysOffsets[i] = KeysOffsets[i - 1]; } var nodeSize = SizeOf.NodeEntry(_pageMaxSpace, key, len); var node = AllocateNewNode(index, key, nodeSize); if (key.Options == SliceOptions.Key) key.CopyTo((byte*)node + Constants.NodeHeaderSize); if (len < 0) // branch or overflow { Debug.Assert(pageNumber != -1); node->PageNumber = pageNumber; node->Flags = NodeFlags.PageRef; return null; // write nothing here } Debug.Assert(key.Options == SliceOptions.Key); var dataPos = (byte*)node + Constants.NodeHeaderSize + key.Size; node->DataSize = len; node->Flags = NodeFlags.Data; return dataPos; }
private static ByteStringContext.ExternalScope CreateCounterKeySlice(DocumentsOperationContext context, ByteString buffer, Slice documentIdPrefix, Slice counterName, out Slice counterKeySlice) { var scope = Slice.External(context.Allocator, buffer.Ptr, buffer.Length, out counterKeySlice); documentIdPrefix.CopyTo(buffer.Ptr); counterName.CopyTo(buffer.Ptr + documentIdPrefix.Size); return(scope); }
public void Add(Slice key, Slice value) { if (!value.HasValue) { ThrowNullReferenceException(); } using (DirectAdd(key, value.Size, out byte *ptr)) value.CopyTo(ptr); }
public void TestCopyToCopiesAllSliceElementsToCorrectLocationInDestination() { var list = new[] { 0, 1, 2, 3, 4 }; var slice = new Slice <int>(list, 2, 2); var expected = new[] { 0, 2, 3 }; var actual = new int[3]; slice.CopyTo(actual, 1); CollectionAssert.AreEqual(expected, actual); }
public void MultiAdd(Transaction tx, Slice key, Slice value, ushort?version = null) { if (value == null) { throw new ArgumentNullException("value"); } if (value.Size > tx.DataPager.MaxNodeSize) { throw new ArgumentException( "Cannot add a value to child tree that is over " + tx.DataPager.MaxNodeSize + " bytes in size", "value"); } if (value.Size == 0) { throw new ArgumentException("Cannot add empty value to child tree"); } State.IsModified = true; Lazy <Cursor> lazy; var page = FindPageFor(tx, key, out lazy); if (page == null || page.LastMatch != 0) { var ptr = DirectAdd(tx, key, value.Size, version: version); value.CopyTo(ptr); return; } page = tx.ModifyPage(page.PageNumber, page); var item = page.GetNode(page.LastSearchPosition); CheckConcurrency(key, version, item->Version, TreeActionType.Add); var existingValue = new Slice(DirectRead(tx, key), (ushort)item->DataSize); if (existingValue.Compare(value, _cmp) == 0) { return; //nothing to do, the exact value is already there } if (item->Flags == NodeFlags.MultiValuePageRef) { var tree = OpenOrCreateMultiValueTree(tx, key, item); tree.DirectAdd(tx, value, 0); } else // need to turn to tree { var tree = Create(tx, _cmp, TreeFlags.MultiValue); var current = NodeHeader.GetData(tx, item); tree.DirectAdd(tx, current, 0); tree.DirectAdd(tx, value, 0); tx.AddMultiValueTree(this, key, tree); // we need to record that we switched to tree mode here, so the next call wouldn't also try to create the tree again DirectAdd(tx, key, sizeof(TreeRootHeader), NodeFlags.MultiValuePageRef); } }
public void MultiAdd(Transaction tx, Slice key, Slice value) { if (value == null) { throw new ArgumentNullException("value"); } if (value.Size > tx.Pager.MaxNodeSize) { throw new ArgumentException("Cannot add a value to child tree that is over " + tx.Pager.MaxNodeSize + " bytes in size", "value"); } if (value.Size == 0) { throw new ArgumentException("Cannot add empty value to child tree"); } using (var cursor = tx.NewCursor(this)) { var page = FindPageFor(tx, key, cursor); if (page == null || page.LastMatch != 0) { var ptr = DirectAdd(tx, key, value.Size); value.CopyTo(ptr); return; } var txInfo = tx.GetTreeInformation(this); page = tx.ModifyCursor(txInfo, cursor); var item = page.GetNode(page.LastSearchPosition); if (item->Flags == NodeFlags.MultiValuePageRef) { var tree = OpenOrCreateMultiValueTree(tx, key, item); tree.DirectAdd(tx, value, 0); } else // need to turn to tree { var tree = Create(tx, _cmp, TreeFlags.MultiValue); var current = NodeHeader.GetData(tx, item); tree.DirectAdd(tx, current, 0); tree.DirectAdd(tx, value, 0); tx.AddMultiValueTree(this, key, tree); DirectAdd(tx, key, sizeof(TreeRootHeader)); cursor.Clear(); page = FindPageFor(tx, key, cursor); Debug.Assert(page.LastMatch == 0); page.GetNode(page.LastSearchPosition)->Flags = NodeFlags.MultiValuePageRef; } } }
public void Add(Slice key, Slice value) { if (!value.HasValue) { throw new ArgumentNullException(nameof(value)); } State.IsModified = true; var pos = DirectAdd(key, value.Size); value.CopyTo(pos); }
public void Add(Slice key, Slice value, ushort?version = null) { if (value == null) { throw new ArgumentNullException("value"); } State.IsModified = true; var pos = DirectAdd(key, value.Size, version: version); value.CopyTo(pos); }
private static ByteStringContext <ByteStringMemoryCache> .ExternalScope CreateTimeSeriesKeyPrefixSlice(DocumentsOperationContext context, ByteString buffer, Slice documentIdPrefix, Slice timeSeriesName, out Slice timeSeriesPrefixSlice) { documentIdPrefix.CopyTo(buffer.Ptr); timeSeriesName.CopyTo(buffer.Ptr + documentIdPrefix.Size); int pos = documentIdPrefix.Size + timeSeriesName.Size; buffer.Ptr[pos++] = SpecialChars.RecordSeparator; var scope = Slice.External(context.Allocator, buffer.Ptr, pos, out timeSeriesPrefixSlice); return(scope); }
/// <summary> /// Transform into the flat variant /// </summary> private void Flatten() { if (_variant == Variant.Flat) { return; } _chars = new char[_left.Length + _right.Length + 64]; _variant = Variant.Flat; _left.CopyTo(_chars.Span); _right.CopyTo(_chars.Span[_left.Length..]);
internal static byte[] PrepareValueForAtomicOperation(Slice value, int size) { if (value.Count >= size) { // truncate if needed return(value.GetBytes(0, size)); } // pad with zeroes var tmp = new byte[size]; value.CopyTo(tmp, 0); return(tmp); }
public void SliceFromNativePtr_CopyToArraySegmentTooSmall() { var origBuffer = GetTestBuffer(100); var gcHandle = GCHandle.Alloc(origBuffer, GCHandleType.Pinned); try { var slice = new Slice(gcHandle.AddrOfPinnedObject(), origBuffer.Length); var tooSmall = new byte[origBuffer.Length - 1]; Assert.Catch(typeof(ArgumentException), () => slice.CopyTo(new ArraySegment <byte>(tooSmall))); } finally { gcHandle.Free(); } }
/// <summary>Copy a slice into the buffer, with optional alignment, and return a new identical slice.</summary> /// <param name="data">Data to copy in the buffer</param> /// <param name="aligned">If true, align the index of first byte of the slice with a multiple of 8 bytes</param> /// <returns>Slice that is the equivalent of <paramref name="data"/>, backed by the buffer.</returns> public Slice Intern(Slice data, bool aligned = false) { if (data.Count == 0) { // transform into the corresponding Slice.Nil / Slice.Empty singleton return(data.Memoize()); } data.EnsureSliceIsValid(); // allocate the slice var slice = Allocate(data.Count, aligned); data.CopyTo(slice.Span); return(slice); }
/// <summary>Copy a slice into the buffer, immediately followed by a suffix, and return a new slice that is the concatenation of the two.</summary> /// <param name="data">Data to copy in the buffer</param> /// <param name="suffix">Suffix to copy immediately after <paramref name="data"/>.</param> /// <param name="aligned">If true, align the index of first byte of the slice with a multiple of 8 bytes</param> /// <returns>Slice that is the equivalent of <paramref name="data"/> plus <paramref name="suffix"/>, backed by the buffer.</returns> /// <remarks>When <paramref name="data"/> is empty, <paramref name="suffix"/> is returned without being copied to the buffer itself.</remarks> internal Slice Intern(Slice data, Slice suffix, bool aligned = false) { if (data.Count == 0) { // note: we don't memoize the suffix, because in most case, it comes from a constant, and it would be a waste to copy it other and other again... return(suffix.Count > 0 ? suffix : data.Array == null ? default : Slice.Empty); } data.EnsureSliceIsValid(); suffix.EnsureSliceIsValid(); var slice = Allocate(data.Count + suffix.Count, aligned); data.CopyTo(slice.Span); suffix.CopyTo(slice.Span.Slice(data.Count)); return(slice); }
public void SliceFromNativePtr_CopyToArraySegment(int bufferLength) { var origBuffer = GetTestBuffer(bufferLength); var gcHandle = GCHandle.Alloc(origBuffer, GCHandleType.Pinned); try { var slice = new Slice(gcHandle.AddrOfPinnedObject(), origBuffer.Length); Assert.AreEqual(bufferLength, slice.Length); var newBuffer = new byte[bufferLength]; slice.CopyTo(new ArraySegment <byte>(newBuffer)); CollectionAssert.AreEqual(origBuffer, newBuffer); } finally { gcHandle.Free(); } }
public void Delete(DocumentsOperationContext context, CollectionName collectionName, Slice loweredKey) { var configuration = GetVersioningConfiguration(collectionName); if (configuration.Active == false) { return; } if (configuration.PurgeOnDelete == false) { return; } var table = context.Transaction.InnerTransaction.OpenTable(DocsSchema, RevisionDocuments); var prefixKeyMem = context.Allocator.Allocate(loweredKey.Size + 1); loweredKey.CopyTo(0, prefixKeyMem.Ptr, 0, loweredKey.Size); prefixKeyMem.Ptr[loweredKey.Size] = (byte)30; // the record separator var prefixSlice = new Slice(SliceOptions.Key, prefixKeyMem); table.DeleteForwardFrom(DocsSchema.Indexes[KeyAndEtagSlice], prefixSlice, long.MaxValue); DeleteCountOfRevisions(context, prefixSlice); }
public void TestSlice() { BufferAllocator<float> bufferAllocator = new BufferAllocator<float>(FloatNumSlices * 2048, 1, sizeof(float)); Buf<float> buffer = bufferAllocator.Allocate(); Slice<Sample, float> slice = new Slice<Sample, float>(buffer, 0, FloatNumSlices, FloatSliverSize); HoloDebug.Assert(slice.Duration == FloatNumSlices); HoloDebug.Assert(slice.IsEmpty() == false); HoloDebug.Assert(slice.SliverSize == FloatSliverSize); var halfDuration = (FloatNumSlices / 2); Slice<Sample, float> prefixSlice = slice.Subslice(0, halfDuration); Slice<Sample, float> prefixSlice2 = slice.SubsliceOfDuration(halfDuration); Slice<Sample, float> suffixSlice = slice.Subslice(halfDuration, halfDuration); Slice<Sample, float> suffixSlice2 = slice.SubsliceStartingAt(halfDuration); HoloDebug.Assert(prefixSlice.Precedes(suffixSlice)); HoloDebug.Assert(prefixSlice.Precedes(suffixSlice2)); HoloDebug.Assert(prefixSlice2.Precedes(suffixSlice)); HoloDebug.Assert(prefixSlice2.Precedes(suffixSlice2)); PopulateFloatSlice(slice); Buf<float> buffer2 = bufferAllocator.Allocate(); Slice<Sample, float> slice2 = new Slice<Sample, float>(buffer2, 0, FloatNumSlices, FloatSliverSize); slice.CopyTo(slice2); VerifySlice(slice2); }
public void MultiAdd(Transaction tx, Slice key, Slice value, ushort? version = null) { if (value == null) throw new ArgumentNullException("value"); if (value.Size > tx.DataPager.MaxNodeSize) throw new ArgumentException( "Cannot add a value to child tree that is over " + tx.DataPager.MaxNodeSize + " bytes in size", "value"); if (value.Size == 0) throw new ArgumentException("Cannot add empty value to child tree"); State.IsModified = true; Lazy<Cursor> lazy; var page = FindPageFor(tx, key, out lazy); if (page == null || page.LastMatch != 0) { var ptr = DirectAdd(tx, key, value.Size, version: version); value.CopyTo(ptr); return; } page = tx.ModifyPage(page.PageNumber, page); var item = page.GetNode(page.LastSearchPosition); CheckConcurrency(key, version, item->Version, TreeActionType.Add); var existingValue = new Slice(DirectRead(tx, key), (ushort) item->DataSize); if (existingValue.Compare(value, _cmp) == 0) return; //nothing to do, the exact value is already there if (item->Flags == NodeFlags.MultiValuePageRef) { var tree = OpenOrCreateMultiValueTree(tx, key, item); tree.DirectAdd(tx, value, 0); } else // need to turn to tree { var tree = Create(tx, _cmp, TreeFlags.MultiValue); var current = NodeHeader.GetData(tx, item); tree.DirectAdd(tx, current, 0); tree.DirectAdd(tx, value, 0); tx.AddMultiValueTree(this, key, tree); // we need to record that we switched to tree mode here, so the next call wouldn't also try to create the tree again DirectAdd(tx, key, sizeof (TreeRootHeader), NodeFlags.MultiValuePageRef); } }
public void WritePrefix(Slice prefix, int prefixId) { var prefixNodeSize = Constants.PrefixNodeHeaderSize + prefix.Size; prefixNodeSize += prefixNodeSize & 1; var prefixNodeOffset = (ushort)(Upper - prefixNodeSize); Upper = prefixNodeOffset; Debug.Assert(_prefixSection->NextPrefixId == prefixId); if (_prefixSection->PrefixOffsets[prefixId] != 0) throw new InvalidOperationException(string.Format("Cannot write a prefix '{0}' at the following offset position: {1} because it's already taken by another prefix. The offset for the prefix {1} is {2}. ", prefix, prefixId, _prefixSection->PrefixOffsets[prefixId])); _prefixSection->PrefixOffsets[prefixId] = prefixNodeOffset; var prefixNodeHeader = (PrefixNodeHeader*)(_base + prefixNodeOffset); prefixNodeHeader->PrefixLength = prefix.Size; prefix.CopyTo((byte*)prefixNodeHeader + Constants.PrefixNodeHeaderSize); _prefixSection->NextPrefixId++; }
private NodeHeader* CreateNode(int index, Slice key, NodeFlags flags, int len, ushort previousNodeVersion) { Debug.Assert(index <= NumberOfEntries && index >= 0); Debug.Assert(IsBranch == false || index != 0 || key.Size == 0);// branch page's first item must be the implicit ref if (HasSpaceFor(key, len) == false) throw new InvalidOperationException("The page is full and cannot add an entry, this is probably a bug"); // move higher pointers up one slot for (int i = NumberOfEntries; i > index; i--) { KeysOffsets[i] = KeysOffsets[i - 1]; } var nodeSize = SizeOf.NodeEntry(PageMaxSpace, key, len); var node = AllocateNewNode(index, key, nodeSize, previousNodeVersion); if (key.Options == SliceOptions.Key) key.CopyTo((byte*)node + Constants.NodeHeaderSize); node->Flags = flags; return node; }
/// <summary>Creates a new binary buffer, initialized by copying pre-existing data</summary> /// <param name="prefix">Data that will be copied at the start of the buffer</param> /// <param name="capacity">Optional initial capacity of the buffer</param> /// <remarks>The cursor will already be placed at the end of the prefix</remarks> public SliceWriter(Slice prefix, int capacity = 0) { if (capacity < 0) throw new ArgumentException("Capacity must be a positive integer.", "capacity"); int n = prefix.Count; Contract.Assert(n >= 0); if (capacity == 0) { // most frequent usage is to add a packed integer at the end of a prefix capacity = SliceHelpers.Align(n + 8); } else { capacity = Math.Max(capacity, n); } var buffer = new byte[capacity]; if (n > 0) prefix.CopyTo(buffer, 0); this.Buffer = buffer; this.Position = n; }
public void Add(Transaction tx, Slice key, Slice value, ushort? version = null) { if (value == null) throw new ArgumentNullException("value"); State.IsModified = true; var pos = DirectAdd(tx, key, value.Size, version: version); value.CopyTo(pos); }