summaryrefslogtreecommitdiffstats
path: root/libparc/parc/algol/parc_Buffer.c
diff options
context:
space:
mode:
authorLuca Muscariello <lumuscar+fdio@cisco.com>2017-02-23 17:01:02 +0100
committerLuca Muscariello <lumuscar+fdio@cisco.com>2017-02-23 17:21:02 +0100
commitec688b4723a041044226358bcd4dd6e2da39da49 (patch)
tree3a244c48d1eb9e4d90f9050fd1a61ae5c0327526 /libparc/parc/algol/parc_Buffer.c
parent9b30fc10fb1cbebe651e5a107e8ca5b24de54675 (diff)
Initial commit: cframework. Longbow and Libparc
Change-Id: I90378dbd30da6033b20fb1f829b3b822cf366c59 Signed-off-by: Luca Muscariello <lumuscar+fdio@cisco.com>
Diffstat (limited to 'libparc/parc/algol/parc_Buffer.c')
-rwxr-xr-xlibparc/parc/algol/parc_Buffer.c1072
1 files changed, 1072 insertions, 0 deletions
diff --git a/libparc/parc/algol/parc_Buffer.c b/libparc/parc/algol/parc_Buffer.c
new file mode 100755
index 00000000..b2ca3577
--- /dev/null
+++ b/libparc/parc/algol/parc_Buffer.c
@@ -0,0 +1,1072 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ */
+#include <config.h>
+#include <ctype.h>
+
+#include <LongBow/runtime.h>
+#include <LongBow/debugging.h>
+
+#include <parc/algol/parc_Object.h>
+#include <parc/algol/parc_Buffer.h>
+#include <parc/algol/parc_ByteArray.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_DisplayIndented.h>
+#include <parc/algol/parc_HashCode.h>
+
+struct parc_buffer {
+ PARCByteArray *array;
+
+ size_t capacity;
+
+ size_t arrayOffset; // The offset within this buffer's backing PARCByteArray of the first element.
+
+ size_t position; // The index, relative to arrayOffset, of the next byte to be read from or written to this buffer.
+
+ /**
+ * The index, relative to arrayOffset, of the last position that cannot be read or written.
+ */
+ size_t limit;
+ /**
+ * A buffer's mark is the index, relative to arrayOffset, to which its position will be set when the reset function is invoked.
+ * The mark is not always defined, but when it is defined it is never negative and is never greater than the position.
+ * If the mark is defined then it is discarded when the position or the limit is adjusted to a value smaller than the mark.
+ * If the mark is not defined then invoking the reset function causes a trap.
+ */
+ size_t mark;
+};
+
+static inline void
+_discardMark(PARCBuffer *buffer)
+{
+ buffer->mark = SIZE_MAX;
+}
+
+static inline bool
+_markIsDiscarded(const PARCBuffer *buffer)
+{
+ return buffer->mark == SIZE_MAX;
+}
+
+static inline void
+_trapIfIndexExceedsLimit(const PARCBuffer *buffer, const size_t index)
+{
+ trapOutOfBoundsIf(index > buffer->limit, "PARCBuffer limit at %zd, attempted to access at %zd",
+ parcBuffer_Limit(buffer), index);
+}
+
+static inline void
+_trapIfBufferUnderflow(const PARCBuffer *buffer, const size_t requiredRemaining)
+{
+ _trapIfIndexExceedsLimit(buffer, buffer->position + requiredRemaining);
+}
+
+static inline size_t
+_effectiveIndex(const PARCBuffer *buffer, const size_t index)
+{
+ return buffer->arrayOffset + index;
+}
+
+static inline size_t
+_effectivePosition(const PARCBuffer *buffer)
+{
+ return buffer->arrayOffset + parcBuffer_Position(buffer);
+}
+
+#ifdef PARCLibrary_DISABLE_VALIDATION
+# define _optionalAssertInvariants(_instance_)
+#else
+# define _optionalAssertInvariants(_instance_) _assertInvariants(_instance_)
+
+static inline void
+_assertInvariants(const PARCBuffer *buffer)
+{
+ // 0 <= mark <= position <= limit <= capacity
+ assertTrue(0 <= buffer->mark,
+ "Expected 0 <= mark (%zd)", buffer->mark);
+ assertTrue(_markIsDiscarded(buffer) || buffer->mark <= buffer->position,
+ "Expected mark (%zd) <= position (%zd)", buffer->mark, buffer->position);
+ assertTrue(buffer->position <= buffer->limit,
+ "Expected position (%zd) <= limit (%zd)", buffer->position, buffer->limit);
+ assertTrue(buffer->limit <= buffer->capacity,
+ "Expected limit (%zd) <= capacity (%zd)", buffer->limit, buffer->capacity);
+ assertTrue((buffer->arrayOffset + buffer->capacity) <= parcByteArray_Capacity(buffer->array),
+ "Expected (%zd + %zd) <= %zd",
+ buffer->arrayOffset, buffer->capacity, parcByteArray_Capacity(buffer->array));
+}
+#endif
+
+static inline int
+_digittoint(char digit)
+{
+ int result = -1;
+
+ int value = digit - '0';
+
+ if ((unsigned) value < 10) {
+ result = value;
+ } else {
+ value = digit - 'a';
+ if ((unsigned) value < 6) {
+ result = value + 10;
+ } else {
+ value = digit - 'A';
+ if ((unsigned) value < 6) {
+ result = value + 10;
+ }
+ }
+ }
+
+ return result;
+}
+
+// This hexadecimal parsing code needs to be fixed - see FogBugz:3598
+
+static char *_hexDigitsUpper = "0123456789ABCDEF";
+static char *_hexDigitsLower = "0123456789abcdef";
+
+static inline char
+_fromHexDigit(char hexDigit)
+{
+ for (char i = 0; i < 16; i++) {
+ if (hexDigit == _hexDigitsLower[(int) i] || hexDigit == _hexDigitsUpper[(int) i]) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+static inline uint8_t
+_hexByte(const char *hexByte)
+{
+ uint8_t result = (uint8_t) (_fromHexDigit(hexByte[0]) << 4) | _fromHexDigit(hexByte[1]);
+ return result;
+}
+
+static inline char *
+_parcBuffer_CheckValidity(const PARCBuffer *buffer)
+{
+ char *result = NULL;
+
+ if (buffer != NULL) {
+ if (parcObject_IsValid(buffer)) {
+ if (parcByteArray_IsValid(buffer->array)) {
+ // 0 <= mark <= position <= limit <= capacity
+ if (_markIsDiscarded(buffer) || buffer->mark <= buffer->position) {
+ if (buffer->position <= buffer->limit) {
+ if (buffer->limit <= buffer->capacity) {
+ if ((buffer->arrayOffset + buffer->capacity) <= parcByteArray_Capacity(buffer->array)) {
+ result = NULL;
+ } else {
+ result = "PARCBuffer offset+capacity exceeds the capacity of the underlying PARCByteArray";
+ }
+ } else {
+ result = "PARCBuffer limit exceeds the capacity.";
+ }
+ } else {
+ result = "PARCBuffer position exceeds the limit.";
+ }
+ } else {
+ result = "PARCBuffer mark exceeds the current position";
+ }
+ } else {
+ result = "PARCBuffer underlying PARCByteArray is invalid";
+ }
+ } else {
+ result = "PARCBuffer is an invalid PARCObject.";
+ }
+ } else {
+ result = "PARCBuffer is NULL";
+ }
+
+ return result;
+}
+
+void
+parcBuffer_AssertValid(const PARCBuffer *buffer)
+{
+ char *explanation = _parcBuffer_CheckValidity(buffer);
+
+ trapIllegalValueIf(explanation != NULL, "PARCBuffer@%p %s.", (void *) buffer, explanation);
+}
+
+bool
+parcBuffer_IsValid(const PARCBuffer *buffer)
+{
+ bool result = false;
+
+ if (buffer != NULL) {
+ if (parcObject_IsValid(buffer)) {
+ if (parcByteArray_IsValid(buffer->array)) {
+ // 0 <= mark <= position <= limit <= capacity
+ if (_markIsDiscarded(buffer) || buffer->mark <= buffer->position) {
+ if (buffer->position <= buffer->limit) {
+ if (buffer->limit <= buffer->capacity) {
+ if ((buffer->arrayOffset + buffer->capacity) <= parcByteArray_Capacity(buffer->array)) {
+ result = true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+static bool
+_parcBuffer_Destructor(PARCBuffer **bufferPtr)
+{
+ PARCBuffer *buffer = *bufferPtr;
+
+ parcByteArray_Release(&buffer->array);
+ return true;
+}
+
+parcObject_Override(PARCBuffer, PARCObject,
+ .destructor = (PARCObjectDestructor *) _parcBuffer_Destructor,
+ .copy = (PARCObjectCopy *) parcBuffer_Copy,
+ .toString = (PARCObjectToString *) parcBuffer_ToString,
+ .equals = (PARCObjectEquals *) parcBuffer_Equals,
+ .compare = (PARCObjectCompare *) parcBuffer_Compare,
+ .hashCode = (PARCObjectHashCode *) parcBuffer_HashCode,
+ .display = (PARCObjectDisplay *) parcBuffer_Display);
+
+/**
+ * Initialise a parcBuffer instance.
+ *
+ * The buffer's offset, position, limit and capacity are set to the given values.
+ * The mark is made invalid.
+ *
+ * @return The same pointer as `result`.
+ */
+static PARCBuffer *
+_parcBuffer_Init(PARCBuffer *result, PARCByteArray *array, size_t offset, size_t position, size_t limit, size_t capacity)
+{
+ result->array = array;
+ result->arrayOffset = offset;
+ result->position = position;
+ result->limit = limit;
+ result->capacity = capacity;
+ _discardMark(result);
+
+ parcBuffer_OptionalAssertValid(result);
+
+ return result;
+}
+
+static inline PARCBuffer *
+_parcBuffer_getInstance(void)
+{
+ PARCBuffer *result = parcObject_CreateInstance(PARCBuffer);
+
+ return result;
+}
+
+static inline size_t
+_computeNewLimit(size_t oldCapacity, size_t oldLimit, size_t newCapacity)
+{
+ size_t result = newCapacity;
+
+ bool limitIsAtCapacity = (oldLimit == oldCapacity);
+
+ if (newCapacity > oldCapacity) {
+ if (limitIsAtCapacity) {
+ result = newCapacity;
+ } else {
+ result = oldLimit;
+ }
+ } else {
+ if (limitIsAtCapacity) {
+ result = newCapacity;
+ } else {
+ result = (oldLimit < newCapacity) ? oldLimit : newCapacity;
+ }
+ }
+
+ return result;
+}
+
+static inline size_t
+_computeNewMark(size_t oldMark, size_t newLimit, size_t newCapacity)
+{
+ size_t result = oldMark;
+
+ if (oldMark != SIZE_MAX) {
+ if (oldMark > newCapacity) {
+ result = SIZE_MAX;
+ } else {
+ result = oldMark;
+ }
+
+ if (result > newLimit) {
+ result = SIZE_MAX;
+ }
+ }
+
+ return result;
+}
+
+PARCBuffer *
+parcBuffer_WrapByteArray(PARCByteArray *byteArray, size_t position, size_t limit)
+{
+ PARCBuffer *result = NULL;
+
+ // The limit cannot exceed the capacity of the PARCByteArray.
+ if (limit > parcByteArray_Capacity(byteArray)) {
+ return NULL;
+ }
+ if (byteArray != NULL) {
+ result = _parcBuffer_getInstance();
+
+ if (result != NULL) {
+ return _parcBuffer_Init(result,
+ parcByteArray_Acquire(byteArray),
+ 0, position, limit,
+ parcByteArray_Capacity(byteArray));
+ }
+ }
+
+ return result;
+}
+
+PARCBuffer *
+parcBuffer_Resize(PARCBuffer *buffer, size_t newCapacity)
+{
+ parcBuffer_OptionalAssertValid(buffer);
+
+ PARCByteArray *newArray = parcByteArray_Allocate(newCapacity);
+ if (newArray == NULL) {
+ return NULL;
+ }
+
+ size_t numberOfBytesToCopy = parcBuffer_Capacity(buffer);
+ if (numberOfBytesToCopy > newCapacity) {
+ numberOfBytesToCopy = newCapacity;
+ }
+
+ parcByteArray_PutBytes(newArray, 0, numberOfBytesToCopy, &parcByteArray_Array(buffer->array)[buffer->arrayOffset]);
+
+ parcByteArray_Release(&buffer->array);
+
+ buffer->array = newArray;
+ buffer->arrayOffset = 0;
+ buffer->limit = _computeNewLimit(buffer->capacity, buffer->limit, newCapacity);
+ buffer->mark = _computeNewMark(buffer->mark, buffer->limit, newCapacity);
+ buffer->capacity = newCapacity;
+ buffer->position = (buffer->position < buffer->limit) ? buffer->position : buffer->limit;
+
+ parcBuffer_OptionalAssertValid(buffer);
+
+ return buffer;
+}
+
+PARCBuffer *
+parcBuffer_Allocate(size_t capacity)
+{
+ PARCByteArray *array = parcByteArray_Allocate(capacity);
+
+ if (array != NULL) {
+ PARCBuffer *result = _parcBuffer_getInstance();
+ if (result != NULL) {
+ return _parcBuffer_Init(result, array, 0, 0, capacity, capacity);
+ }
+ parcByteArray_Release(&array);
+ }
+
+ return NULL;
+}
+
+PARCBuffer *
+parcBuffer_Wrap(void *array, size_t arrayLength, size_t position, size_t limit)
+{
+ PARCBuffer *result = NULL;
+
+ if (array != NULL) {
+ PARCByteArray *byteArray = parcByteArray_Wrap(arrayLength, array);
+
+ if (byteArray != NULL) {
+ result = parcBuffer_WrapByteArray(byteArray, position, limit);
+ parcByteArray_Release(&byteArray);
+ }
+ }
+
+ return result;
+}
+
+PARCBuffer *
+parcBuffer_WrapCString(char *string)
+{
+ size_t length = strlen(string);
+ return parcBuffer_Wrap(string, length, 0, length);
+}
+
+PARCBuffer *
+parcBuffer_AllocateCString(const char *string)
+{
+ size_t length = strlen(string);
+ PARCBuffer *buffer = parcBuffer_Allocate(length + 1);
+ parcBuffer_PutArray(buffer, length, (const uint8_t *) string);
+ parcBuffer_PutUint8(buffer, 0);
+ parcBuffer_SetPosition(buffer, buffer->position - 1);
+ parcBuffer_Flip(buffer);
+
+ return buffer;
+}
+
+PARCBuffer *
+parcBuffer_ParseHexString(const char *hexString)
+{
+ size_t length = strlen(hexString);
+
+ // The hex string must be an even length greater than zero.
+ if (length > 0 && (length % 2) == 1) {
+ return NULL;
+ }
+
+ PARCBuffer *result = parcBuffer_Allocate(length);
+ for (size_t i = 0; i < length; i += 2) {
+ parcBuffer_PutUint8(result, _hexByte(&hexString[i]));
+ }
+
+ return result;
+}
+
+PARCBuffer *
+parcBuffer_CreateFromArray(const void *bytes, const size_t length)
+{
+ assertTrue(length == 0 || bytes != NULL,
+ "If the byte array is NULL, then length MUST be zero.");
+
+ PARCBuffer *result = parcBuffer_Allocate(length);
+ parcBuffer_PutArray(result, length, bytes);
+
+ return result;
+}
+
+parcObject_ImplementAcquire(parcBuffer, PARCBuffer);
+
+parcObject_ImplementRelease(parcBuffer, PARCBuffer);
+
+size_t
+parcBuffer_Capacity(const PARCBuffer *buffer)
+{
+ parcBuffer_OptionalAssertValid(buffer);
+
+ return buffer->capacity;
+}
+
+PARCBuffer *
+parcBuffer_Clear(PARCBuffer *buffer)
+{
+ parcBuffer_SetPosition(buffer, 0);
+ parcBuffer_SetLimit(buffer, parcBuffer_Capacity(buffer));
+ _discardMark(buffer);
+ return buffer;
+}
+
+bool
+parcBuffer_Equals(const PARCBuffer *x, const PARCBuffer *y)
+{
+ if (x == y) {
+ return true;
+ }
+ if (x == NULL || y == NULL) {
+ return false;
+ }
+
+ return (parcBuffer_Compare(x, y) == 0);
+}
+
+int
+parcBuffer_Compare(const PARCBuffer *x, const PARCBuffer *y)
+{
+ if (x == y) {
+ return 0;
+ }
+
+ if (x == NULL) {
+ return -1;
+ }
+
+ if (y == NULL) {
+ return +1;
+ }
+
+ size_t count = parcBuffer_Remaining(x);
+ if (count > parcBuffer_Remaining(y)) {
+ count = parcBuffer_Remaining(y);
+ }
+
+ int result = 0;
+
+ if (count > 0) {
+ result = memcmp(parcBuffer_Overlay((PARCBuffer *) x, 0), parcBuffer_Overlay((PARCBuffer *) y, 0), count);
+ }
+
+ if (result == 0) {
+ // This is when one buffer is longer than the other, and they are equivalent thus far.
+ ssize_t difference = parcBuffer_Remaining(x) - parcBuffer_Remaining(y);
+ if (difference > 0) {
+ result = +1;
+ } else if (difference < 0) {
+ result = -1;
+ }
+ }
+
+ return result;
+}
+
+PARCByteArray *
+parcBuffer_Array(const PARCBuffer *buffer)
+{
+ parcBuffer_OptionalAssertValid(buffer);
+
+ return buffer->array;
+}
+
+PARCBuffer *
+parcBuffer_Duplicate(const PARCBuffer *original)
+{
+ PARCBuffer *result = _parcBuffer_getInstance();
+ if (result != NULL) {
+ _parcBuffer_Init(result,
+ parcByteArray_Acquire(original->array),
+ original->arrayOffset,
+ original->position,
+ parcBuffer_Limit(original),
+ original->capacity);
+
+ _optionalAssertInvariants(result);
+ }
+ return result;
+}
+
+PARCBuffer *
+parcBuffer_Slice(const PARCBuffer *original)
+{
+ PARCBuffer *result = _parcBuffer_getInstance();
+ if (result != NULL) {
+ _parcBuffer_Init(result,
+ parcByteArray_Acquire(original->array),
+ original->arrayOffset + parcBuffer_Position(original),
+ 0,
+ parcBuffer_Limit(original) - parcBuffer_Position(original),
+ parcBuffer_Limit(original) - parcBuffer_Position(original));
+
+ _optionalAssertInvariants(result);
+ }
+ return result;
+}
+
+size_t
+parcBuffer_ArrayOffset(const PARCBuffer *buffer)
+{
+ parcBuffer_OptionalAssertValid(buffer);
+
+ return buffer->arrayOffset;
+}
+
+PARCBuffer *
+parcBuffer_Reset(PARCBuffer *buffer)
+{
+ parcBuffer_OptionalAssertValid(buffer);
+
+ assertFalse(_markIsDiscarded(buffer),
+ "The mark has not been set");
+ buffer->position = buffer->mark;
+
+ _optionalAssertInvariants(buffer);
+
+ return buffer;
+}
+
+size_t
+parcBuffer_Limit(const PARCBuffer *buffer)
+{
+ parcBuffer_OptionalAssertValid(buffer);
+
+ return buffer->limit;
+}
+
+PARCBuffer *
+parcBuffer_Mark(PARCBuffer *buffer)
+{
+ parcBuffer_OptionalAssertValid(buffer);
+
+ buffer->mark = buffer->position;
+ _optionalAssertInvariants(buffer);
+ return buffer;
+}
+
+PARCBuffer *
+parcBuffer_SetLimit(PARCBuffer *buffer, size_t newLimit)
+{
+ parcBuffer_OptionalAssertValid(buffer);
+ assertTrue(newLimit <= parcBuffer_Capacity(buffer),
+ "new limit cannot be larger than the capacity");
+
+ if (_markIsDiscarded(buffer)) {
+ buffer->limit = newLimit;
+ _discardMark(buffer);
+ } else {
+ if (newLimit < buffer->position) {
+ buffer->position = newLimit;
+ }
+ if (newLimit < buffer->mark) {
+ _discardMark(buffer);
+ }
+ buffer->limit = newLimit;
+ }
+ _optionalAssertInvariants(buffer);
+ return buffer;
+}
+
+size_t
+parcBuffer_Position(const PARCBuffer *buffer)
+{
+ parcBuffer_OptionalAssertValid(buffer);
+
+ return buffer->position;
+}
+
+size_t
+parcBuffer_Remaining(const PARCBuffer *buffer)
+{
+ parcBuffer_OptionalAssertValid(buffer);
+
+ return buffer->limit - buffer->position;
+}
+
+bool
+parcBuffer_HasRemaining(const PARCBuffer *buffer)
+{
+ parcBuffer_OptionalAssertValid(buffer);
+
+ return parcBuffer_Remaining(buffer) != 0;
+}
+
+PARCBuffer *
+parcBuffer_SetPosition(PARCBuffer *buffer, size_t newPosition)
+{
+ parcBuffer_OptionalAssertValid(buffer);
+
+ assertFalse(newPosition > buffer->limit,
+ "new position cannot be greater the buffer's limit");
+
+ buffer->position = newPosition;
+ if (!_markIsDiscarded(buffer) && newPosition < buffer->mark) {
+ _discardMark(buffer);
+ }
+
+ _optionalAssertInvariants(buffer);
+ return buffer;
+}
+
+PARCBuffer *
+parcBuffer_Rewind(PARCBuffer *buffer)
+{
+ parcBuffer_OptionalAssertValid(buffer);
+
+ buffer->position = 0;
+ _discardMark(buffer);
+
+ _optionalAssertInvariants(buffer);
+ return buffer;
+}
+
+PARCBuffer *
+parcBuffer_Copy(const PARCBuffer *original)
+{
+ parcBuffer_OptionalAssertValid(original);
+
+ PARCBuffer *result = _parcBuffer_getInstance();
+
+ if (result != NULL) {
+ PARCByteArray *array = parcByteArray_Copy(original->array);
+ if (array != NULL) {
+ _parcBuffer_Init(result,
+ array,
+ parcBuffer_ArrayOffset(original),
+ parcBuffer_Position(original),
+ parcBuffer_Limit(original),
+ parcBuffer_Capacity(original));
+ } else {
+ parcBuffer_Release(&result);
+ }
+ }
+
+ return result;
+}
+
+PARCBuffer *
+parcBuffer_Flip(PARCBuffer *result)
+{
+ parcBuffer_OptionalAssertValid(result);
+
+ size_t position = result->position;
+ result->position = 0;
+ result->limit = position;
+
+ _optionalAssertInvariants(result);
+
+ return result;
+}
+
+uint8_t
+parcBuffer_GetAtIndex(const PARCBuffer *buffer, size_t index)
+{
+ parcBuffer_OptionalAssertValid(buffer);
+
+ _trapIfIndexExceedsLimit(buffer, index);
+
+ return parcByteArray_GetByte(buffer->array, _effectiveIndex(buffer, index));
+}
+
+void *
+parcBuffer_Overlay(PARCBuffer *buffer, size_t length)
+{
+ parcBuffer_OptionalAssertValid(buffer);
+ _trapIfBufferUnderflow(buffer, length);
+
+ uint8_t *result = parcByteArray_AddressOfIndex(buffer->array, _effectiveIndex(buffer, parcBuffer_Position(buffer)));
+ buffer->position += length;
+ return result;
+}
+
+uint8_t
+parcBuffer_GetUint8(PARCBuffer *buffer)
+{
+ parcBuffer_OptionalAssertValid(buffer);
+ _trapIfBufferUnderflow(buffer, 1);
+
+ uint8_t result = parcByteArray_GetByte(buffer->array, _effectivePosition(buffer));
+ buffer->position++;
+
+ return result;
+}
+
+PARCBuffer *
+parcBuffer_GetBytes(PARCBuffer *buffer, size_t length, uint8_t array[length])
+{
+ parcBuffer_OptionalAssertValid(buffer);
+ _trapIfBufferUnderflow(buffer, length);
+
+ parcByteArray_GetBytes(buffer->array, _effectivePosition(buffer), length, array);
+ buffer->position += length;
+
+ return buffer;
+}
+
+uint16_t
+parcBuffer_GetUint16(PARCBuffer *buffer)
+{
+ uint8_t high = parcBuffer_GetUint8(buffer);
+ uint8_t low = parcBuffer_GetUint8(buffer);
+
+ uint16_t result = (high << 8) | low;
+
+ return result;
+}
+
+uint32_t
+parcBuffer_GetUint32(PARCBuffer *buffer)
+{
+ uint32_t result = 0;
+ for (int i = 0; i < sizeof(uint32_t); i++) {
+ result = result << 8 | parcBuffer_GetUint8(buffer);
+ }
+ return result;
+}
+
+uint64_t
+parcBuffer_GetUint64(PARCBuffer *buffer)
+{
+ uint64_t result = 0;
+ for (int i = 0; i < sizeof(uint64_t); i++) {
+ result = result << 8 | parcBuffer_GetUint8(buffer);
+ }
+ return result;
+}
+
+PARCBuffer *
+parcBuffer_PutUint8(PARCBuffer *buffer, uint8_t value)
+{
+ parcBuffer_OptionalAssertValid(buffer);
+ assertTrue(parcBuffer_Remaining(buffer) >= 1,
+ "Buffer overflow");
+
+ parcByteArray_PutByte(buffer->array, _effectivePosition(buffer), value);
+ buffer->position++;
+ return buffer;
+}
+
+PARCBuffer *
+parcBuffer_PutUint16(PARCBuffer *buffer, uint16_t value)
+{
+ assertTrue(parcBuffer_Remaining(buffer) >= sizeof(uint16_t),
+ "Buffer overflow");
+
+ parcBuffer_PutUint8(buffer, (value >> 8) & 0xFF);
+ parcBuffer_PutUint8(buffer, value & 0xFF);
+ return buffer;
+}
+
+PARCBuffer *
+parcBuffer_PutUint32(PARCBuffer *buffer, uint32_t value)
+{
+ assertTrue(parcBuffer_Remaining(buffer) >= sizeof(uint32_t),
+ "Buffer overflow");
+ for (int i = sizeof(uint32_t) - 1; i > 0; i--) {
+ uint8_t b = value >> (i * 8) & 0xFF;
+ parcBuffer_PutUint8(buffer, b);
+ }
+ parcBuffer_PutUint8(buffer, value & 0xFF);
+ return buffer;
+}
+
+PARCBuffer *
+parcBuffer_PutUint64(PARCBuffer *buffer, uint64_t value)
+{
+ assertTrue(parcBuffer_Remaining(buffer) >= sizeof(uint64_t),
+ "Buffer overflow");
+ for (int i = sizeof(uint64_t) - 1; i > 0; i--) {
+ uint8_t b = value >> (i * 8) & 0xFF;
+ parcBuffer_PutUint8(buffer, b);
+ }
+ parcBuffer_PutUint8(buffer, value & 0xFF);
+ return buffer;
+}
+
+PARCBuffer *
+parcBuffer_PutAtIndex(PARCBuffer *buffer, size_t index, uint8_t value)
+{
+ parcBuffer_OptionalAssertValid(buffer);
+ assertTrue(_effectiveIndex(buffer, index) < parcBuffer_Limit(buffer), "Buffer overflow");
+
+ parcByteArray_PutByte(buffer->array, _effectiveIndex(buffer, index), value);
+ return buffer;
+}
+
+PARCBuffer *
+parcBuffer_PutArray(PARCBuffer *buffer, size_t arrayLength, const uint8_t array[arrayLength])
+{
+ parcBuffer_OptionalAssertValid(buffer);
+ assertTrue(parcBuffer_Remaining(buffer) >= arrayLength,
+ "Buffer overflow");
+
+ parcByteArray_PutBytes(buffer->array, _effectivePosition(buffer), arrayLength, array);
+ return parcBuffer_SetPosition(buffer, parcBuffer_Position(buffer) + arrayLength);
+}
+
+PARCBuffer *
+parcBuffer_PutCString(PARCBuffer *buffer, const char *string)
+{
+ return parcBuffer_PutArray(buffer, strlen(string) + 1, (const uint8_t *) string);
+}
+
+PARCBuffer *
+parcBuffer_PutBuffer(PARCBuffer *result, const PARCBuffer *buffer)
+{
+ parcBuffer_OptionalAssertValid(buffer);
+ assertTrue(parcBuffer_Remaining(result) >= parcBuffer_Remaining(buffer),
+ "Buffer overflow. %zd bytes remaining, %zd required.", parcBuffer_Remaining(result), parcBuffer_Remaining(buffer));
+
+ size_t length = parcBuffer_Remaining(buffer);
+ parcByteArray_ArrayCopy(result->array, _effectivePosition(result), buffer->array, _effectivePosition(buffer), length);
+ parcBuffer_SetPosition(result, parcBuffer_Position(result) + length);
+ return result;
+}
+
+PARCHashCode
+parcBuffer_HashCode(const PARCBuffer *buffer)
+{
+ PARCHashCode result = 0;
+
+ size_t remaining = parcBuffer_Remaining(buffer);
+ if (remaining > 0) {
+ result = parcHashCode_Hash(parcBuffer_Overlay((PARCBuffer *) buffer, 0), parcBuffer_Remaining(buffer));
+ }
+ return result;
+}
+
+size_t
+parcBuffer_FindUint8(const PARCBuffer *buffer, uint8_t byte)
+{
+ for (size_t i = parcBuffer_Position(buffer); i < parcBuffer_Limit(buffer); i++) {
+ if (parcBuffer_GetAtIndex(buffer, i) == byte) {
+ return i;
+ }
+ }
+ return SIZE_MAX;
+}
+
+char *
+parcBuffer_ToString(const PARCBuffer *buffer)
+{
+ size_t remaining = parcBuffer_Remaining(buffer);
+
+ char *result = parcMemory_Allocate(remaining + 1);
+ if (remaining > 0) {
+ assertNotNull(result, "parcMemory_Allocate returned NULL");
+ if (result != NULL) {
+ memcpy(result, parcBuffer_Overlay((PARCBuffer *) buffer, 0), remaining);
+ }
+ }
+ result[remaining] = 0;
+ return result;
+}
+
+void
+parcBuffer_Display(const PARCBuffer *buffer, int indentation)
+{
+ if (buffer == NULL) {
+ parcDisplayIndented_PrintLine(indentation, "PARCBuffer@NULL");
+ } else {
+ parcDisplayIndented_PrintLine(indentation, "PARCBuffer@%p {", (void *) buffer);
+ parcDisplayIndented_PrintLine(indentation + 1,
+ ".arrayOffset=%zd .position=%zd .limit=%zd .mark=%zd",
+ buffer->arrayOffset, buffer->position, buffer->limit, buffer->mark);
+ parcByteArray_Display(buffer->array, indentation + 1);
+ parcDisplayIndented_PrintLine(indentation, "}");
+ }
+}
+
+// Given a value, return the low nibble as a hex character.
+static char
+_toHexDigit(const char value)
+{
+ return "0123456789ABCDEF"[value & 0xF];
+}
+
+char *
+parcBuffer_ToHexString(const PARCBuffer *buffer)
+{
+ if (buffer == NULL) {
+ return parcMemory_StringDuplicate("null", 4);
+ }
+
+ size_t length = parcBuffer_Remaining(buffer);
+ // Hopefully length is less than (2^(sizeof(size_t)*8) / 2)
+
+ char *result = parcMemory_AllocateAndClear((length * 2) + 1);
+ assertNotNull(result, "parcMemory_AllocateAndClear(%zu) returned NULL", (length * 2) + 1);
+
+ for (size_t i = 0; i < length; i++) {
+ unsigned char byte = parcBuffer_GetAtIndex(buffer, i);
+ result[i * 2] = _toHexDigit(byte >> 4);
+ result[i * 2 + 1] = _toHexDigit(byte);
+ }
+ result[length * 2] = 0;
+
+ return result;
+}
+
+bool
+parcBuffer_SkipOver(PARCBuffer *buffer, size_t length, const uint8_t bytesToSkipOver[length])
+{
+ while (parcBuffer_Remaining(buffer) > 0) {
+ uint8_t character = parcBuffer_GetUint8(buffer);
+ if (memchr(bytesToSkipOver, character, length) == NULL) {
+ parcBuffer_SetPosition(buffer, parcBuffer_Position(buffer) - 1);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+parcBuffer_SkipTo(PARCBuffer *buffer, size_t length, const uint8_t bytesToSkipTo[length])
+{
+ bool result = false;
+
+ while (parcBuffer_Remaining(buffer) > 0) {
+ uint8_t character = parcBuffer_GetUint8(buffer);
+ if (memchr(bytesToSkipTo, character, length) != NULL) {
+ parcBuffer_SetPosition(buffer, parcBuffer_Position(buffer) - 1);
+ result = true;
+ break;
+ }
+ }
+ return result;
+}
+
+uint8_t
+parcBuffer_PeekByte(const PARCBuffer *buffer)
+{
+ return parcBuffer_GetAtIndex(buffer, parcBuffer_Position(buffer));
+}
+
+uint64_t
+parcBuffer_ParseHexNumber(PARCBuffer *buffer)
+{
+ char *bytes = parcBuffer_Overlay(buffer, 0);
+
+ int start = 0;
+ if (parcBuffer_Remaining(buffer) > 2) {
+ if (bytes[0] == '0' && bytes[1] == 'x') {
+ start = 2;
+ }
+ }
+
+ unsigned count = 0;
+ uint64_t result = 0;
+ for (int i = start; i < parcBuffer_Remaining(buffer) && isxdigit(bytes[i]); i++) {
+ result = (result * 16) + _digittoint(bytes[i]);
+ count++;
+ }
+
+ parcBuffer_SetPosition(buffer, parcBuffer_Position(buffer) + start + count);
+
+ return result;
+}
+
+uint64_t
+parcBuffer_ParseDecimalNumber(PARCBuffer *buffer)
+{
+ char *bytes = parcBuffer_Overlay(buffer, 0);
+
+ int start = 0;
+
+ unsigned count = 0;
+ uint64_t result = 0;
+ for (int i = start; i < parcBuffer_Remaining(buffer) && isdigit(bytes[i]); i++) {
+ result = (result * 10) + _digittoint(bytes[i]);
+ count++;
+ }
+
+ parcBuffer_SetPosition(buffer, parcBuffer_Position(buffer) + count);
+
+ return result;
+}
+
+uint64_t
+parcBuffer_ParseNumeric(PARCBuffer *buffer)
+{
+ uint64_t result = 0;
+
+ char *bytes = parcBuffer_Overlay(buffer, 0);
+
+ if (parcBuffer_Remaining(buffer) > 2 && bytes[0] == '0' && bytes[1] == 'x') {
+ result = parcBuffer_ParseHexNumber(buffer);
+ } else {
+ result = parcBuffer_ParseDecimalNumber(buffer);
+ }
+
+ return result;
+}