diff options
Diffstat (limited to 'libparc/parc/algol')
211 files changed, 77597 insertions, 0 deletions
diff --git a/libparc/parc/algol/.gitignore b/libparc/parc/algol/.gitignore new file mode 100644 index 00000000..cf23441d --- /dev/null +++ b/libparc/parc/algol/.gitignore @@ -0,0 +1,4 @@ +parcAlgol_About.c +parcLibrary_About.c +parcLibrary_About.h +parcLibrary diff --git a/libparc/parc/algol/Groups.dox b/libparc/parc/algol/Groups.dox new file mode 100644 index 00000000..f798378c --- /dev/null +++ b/libparc/parc/algol/Groups.dox @@ -0,0 +1,41 @@ +/** +\mainpage +The PARC Library is a collection of data structures, algorithms, +abstractions and generally useful facilities for C programs. + +The most notable facility of the PARC library is the use of `PARCObject` +reference counted allocated memory. + +The general abstractions provided by the library try to follow along with the +Java runtime. +For example, the PARC Library provides `PARCHashMap`, `PARCLinkedList`, +`PARCInputStream`, and so forth. +Ideally, the Java programmer will feel at home with the PARC Library. + +@defgroup types Basic Types +@brief Basic Types + +@defgroup object Reference counted object types +@brief Reference counted object types + +@defgroup datastructures Data Structures +@brief Data structures + +@defgroup inputoutput Input Output +@brief Functions to manage input and output. + +@defgroup memory Memory and Buffer Management +@brief Functions to manage memory and references to memory. + +@defgroup threading Threading and Concurrency +@brief Functions to manage Threading and Concurrency. + +@defgroup security Security +@brief Functions to manage Security. + +@defgroup developer Developer Aids +@brief Functions to provide Developer Aids. + +@defgroup networking Networking and Communication +@brief Functions to manage Networking and Communication. +*/ diff --git a/libparc/parc/algol/internal_parc_Event.c b/libparc/parc/algol/internal_parc_Event.c new file mode 100755 index 00000000..74e917bf --- /dev/null +++ b/libparc/parc/algol/internal_parc_Event.c @@ -0,0 +1,324 @@ +/* + * 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 <LongBow/runtime.h> +#include <parc/algol/parc_Memory.h> +#include "internal_parc_Event.h" + +/** + * Current implementation based on top of libevent2 + */ +#include <event2/event.h> +#include <event2/bufferevent.h> + +void * +internal_parc_alloc(size_t size) +{ + void *pointer; + + pointer = parcMemory_AllocateAndClear(size); + return pointer; +} + +void * +internal_parc_realloc(void *pointer, size_t newSize) +{ + void *newPointer; + newPointer = parcMemory_Reallocate(pointer, newSize); + return newPointer; +} + +// We interpose on the standard "free" interface to protect against freed memory accesses. +void +internal_parc_free(void *ptr) +{ + parcMemory_Deallocate((void **) &ptr); +} + +static int _libeventInitialized = 0; + +void +internal_parc_initializeLibevent(void) +{ + if (_libeventInitialized) { + return; + } + _libeventInitialized = 1; + + // 0x AA BB CC XX + // AA = major + // BB = minor + // CC = patchlevel + // + uint32_t version = event_get_version_number(); + trapIllegalValueIf(version < 0x02001000UL, + "Libevent version must be at least 2.0.16, got %s", + event_get_version()); + + // Make sure libevent uses our memory allocator. + // Libevent allocates an internal object the first time a base is allocated + // that it never releases. In order to ensure our outstanding memory counters + // start at zero we trigger this allocation before interposing our memory allocator. + // + // Create a scheduler event base, an event, then free both of them. + // + struct event_base *evbase = event_base_new(); + assertNotNull(evbase, "Libevent event_base_new returned NULL"); + struct event *event = event_new(evbase, -1, 0, NULL, NULL); + assertNotNull(event, "Libevent event_new returned NULL"); + event_del(event); + event_base_free(evbase); + event_free(event); + + event_set_mem_functions(internal_parc_alloc, + internal_parc_realloc, + internal_parc_free); +} + +PARCEventPriority +internal_libevent_priority_to_PARCEventPriority(short evpriority) +{ + PARCEventPriority priority = 0; + switch (evpriority) { + case 0: priority = PARCEventPriority_Maximum; + break; + case 1: priority = PARCEventPriority_Normal; + break; + case 2: priority = PARCEventPriority_Minimum; + break; + default: + assertTrue(0, "Unknown Libevent priority 0x%x\n", evpriority); + break; + } + return priority; +} + +short +internal_PARCEventPriority_to_libevent_priority(PARCEventPriority priority) +{ + short evpriority = 0; + switch (priority) { + case PARCEventPriority_Maximum: evpriority = 0; + break; + case PARCEventPriority_Normal: evpriority = 1; + break; + case PARCEventPriority_Minimum: evpriority = 2; + break; + default: + assertTrue(0, "Unknown PARCEventPriority 0x%x\n", evpriority); + break; + } + return evpriority; +} + +PARCEventSchedulerDispatchType +internal_eventloop_options_to_PARCEventSchedulerDispatchType(short evoptions) +{ + PARCEventSchedulerDispatchType options = 0; + switch (evoptions) { + case 0: options = PARCEventSchedulerDispatchType_Blocking; + break; + case EVLOOP_ONCE: options = PARCEventSchedulerDispatchType_LoopOnce; + break; + case EVLOOP_NONBLOCK: options = PARCEventSchedulerDispatchType_NonBlocking; + break; + default: + assertTrue(0, "Unknown Libevent dispatcher flag 0x%x\n", evoptions); + break; + } + return options; +} + +short +internal_PARCEventSchedulerDispatchType_to_eventloop_options(PARCEventSchedulerDispatchType options) +{ + short evoptions = 0; + switch (options) { + case PARCEventSchedulerDispatchType_Blocking: evoptions = 0; + break; + case PARCEventSchedulerDispatchType_LoopOnce: evoptions = EVLOOP_ONCE; + break; + case PARCEventSchedulerDispatchType_NonBlocking: evoptions = EVLOOP_NONBLOCK; + break; + default: + assertTrue(0, "Unknown PARCEventSchedulerDispatchType option 0x%x\n", evoptions); + break; + } + return evoptions; +} + +PARCEventQueueOption +internal_bufferevent_options_to_PARCEventQueueOption(short evflags) +{ + PARCEventQueueOption flags = 0; + if (evflags & BEV_OPT_CLOSE_ON_FREE) { + flags |= PARCEventQueueOption_CloseOnFree; + evflags &= ~BEV_OPT_CLOSE_ON_FREE; + } + if (evflags & BEV_OPT_DEFER_CALLBACKS) { + flags |= PARCEventQueueOption_DeferCallbacks; + evflags &= ~BEV_OPT_DEFER_CALLBACKS; + } + assertTrue(evflags == 0, "Unknown Libevent option flag 0x%x\n", evflags); + return flags; +} + +short +internal_PARCEventQueueOption_to_bufferevent_options(PARCEventQueueOption flags) +{ + short evflags = 0; + if (flags & PARCEventQueueOption_CloseOnFree) { + evflags |= BEV_OPT_CLOSE_ON_FREE; + flags &= ~PARCEventQueueOption_CloseOnFree; + } + if (flags & PARCEventQueueOption_DeferCallbacks) { + evflags |= BEV_OPT_DEFER_CALLBACKS; + flags &= ~PARCEventQueueOption_DeferCallbacks; + } + assertTrue(flags == 0, "Unknown PARCEventQueueOption flag 0x%x\n", flags); + return evflags; +} + +PARCEventQueueEventType +internal_bufferevent_type_to_PARCEventQueueEventType(short evtypes) +{ + PARCEventQueueEventType types = 0; + if (evtypes & BEV_EVENT_READING) { + types |= PARCEventQueueEventType_Reading; + evtypes &= ~BEV_EVENT_READING; + } + if (evtypes & BEV_EVENT_WRITING) { + types |= PARCEventQueueEventType_Writing; + evtypes &= ~BEV_EVENT_WRITING; + } + if (evtypes & BEV_EVENT_EOF) { + types |= PARCEventQueueEventType_EOF; + evtypes &= ~BEV_EVENT_EOF; + } + if (evtypes & BEV_EVENT_ERROR) { + types |= PARCEventQueueEventType_Error; + evtypes &= ~BEV_EVENT_ERROR; + } + if (evtypes & BEV_EVENT_TIMEOUT) { + types |= PARCEventQueueEventType_Timeout; + evtypes &= ~BEV_EVENT_TIMEOUT; + } + if (evtypes & BEV_EVENT_CONNECTED) { + types |= PARCEventQueueEventType_Connected; + evtypes &= ~BEV_EVENT_CONNECTED; + } + assertTrue(evtypes == 0, "Unknown Libevent type flag 0x%x\n", evtypes); + return types; +} + +short +internal_PARCEventQueueEventType_to_bufferevent_type(PARCEventQueueEventType types) +{ + short evtypes = 0; + if (types & PARCEventQueueEventType_Reading) { + evtypes |= BEV_EVENT_READING; + types &= ~PARCEventQueueEventType_Reading; + } + if (types & PARCEventQueueEventType_Writing) { + evtypes |= BEV_EVENT_WRITING; + types &= ~PARCEventQueueEventType_Writing; + } + if (types & PARCEventQueueEventType_EOF) { + evtypes |= BEV_EVENT_EOF; + types &= ~PARCEventQueueEventType_EOF; + } + if (types & PARCEventQueueEventType_Error) { + evtypes |= BEV_EVENT_ERROR; + types &= ~PARCEventQueueEventType_Error; + } + if (types & PARCEventQueueEventType_Timeout) { + evtypes |= BEV_EVENT_TIMEOUT; + types &= ~PARCEventQueueEventType_Timeout; + } + if (types & PARCEventQueueEventType_Connected) { + evtypes |= BEV_EVENT_CONNECTED; + types &= ~PARCEventQueueEventType_Connected; + } + assertTrue(types == 0, "Unknown PARCEventQueueEventType 0x%x\n", types); + return evtypes; +} + +PARCEventType +internal_libevent_type_to_PARCEventType(short evtypes) +{ + PARCEventType types = 0; + if (evtypes & EV_TIMEOUT) { + types |= PARCEventType_Timeout; + evtypes &= ~EV_TIMEOUT; + } + if (evtypes & EV_READ) { + types |= PARCEventType_Read; + evtypes &= ~EV_READ; + } + if (evtypes & EV_WRITE) { + types |= PARCEventType_Write; + evtypes &= ~EV_WRITE; + } + if (evtypes & EV_SIGNAL) { + types |= PARCEventType_Signal; + evtypes &= ~EV_SIGNAL; + } + if (evtypes & EV_PERSIST) { + types |= PARCEventType_Persist; + evtypes &= ~EV_PERSIST; + } + if (evtypes & EV_ET) { + types |= PARCEventType_EdgeTriggered; + evtypes &= ~EV_ET; + } + assertTrue(evtypes == 0, "Unknown Libevent event type 0x%x\n", evtypes); + return types; +} + +short +internal_PARCEventType_to_libevent_type(PARCEventType types) +{ + short evtypes = 0; + if (types & PARCEventType_Timeout) { + evtypes |= EV_TIMEOUT; + types &= ~PARCEventType_Timeout; + } + if (types & PARCEventType_Read) { + evtypes |= EV_READ; + types &= ~PARCEventType_Read; + } + if (types & PARCEventType_Write) { + evtypes |= EV_WRITE; + types &= ~PARCEventType_Write; + } + if (types & PARCEventType_Signal) { + evtypes |= EV_SIGNAL; + types &= ~PARCEventType_Signal; + } + if (types & PARCEventType_Persist) { + evtypes |= EV_PERSIST; + types &= ~PARCEventType_Persist; + } + if (types & PARCEventType_EdgeTriggered) { + evtypes |= EV_ET; + types &= ~PARCEventType_EdgeTriggered; + } + assertTrue(types == 0, "Unknown Libevent event type 0x%x\n", types); + return evtypes; +} diff --git a/libparc/parc/algol/internal_parc_Event.h b/libparc/parc/algol/internal_parc_Event.h new file mode 100755 index 00000000..ab157a56 --- /dev/null +++ b/libparc/parc/algol/internal_parc_Event.h @@ -0,0 +1,168 @@ +/* + * 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. + */ + +/** + * @file parc_EventQueue.h + * @ingroup events + * @brief Queue buffer events + * + * Provides a facade implementing many regularly available event functions. + * This is an interface that software implementors may use to substitute + * different kinds of underlying implementations of these event management functions. + * Notable examples are libevent and libev. + * + */ +#ifndef libparc_internal_parc_Event_h +#define libparc_internal_parc_Event_h + +#include <parc/algol/parc_EventScheduler.h> +#include <parc/algol/parc_EventQueue.h> + +/** + * Map alloc() method call to a PARC internal memory method + * + * @param [in] size of memory to allocate + * @returns NULL on error, memory pointer on success. + * + * Example: + * @code + * { + * return internal_parc_alloc(1024); + * } + * @endcode + * + */ +void *internal_parc_alloc(size_t size); + +/** + * Map realloc() method call to a PARC internal memory method + * + * @param [in] pointer to memory to reallocate + * @param [in] newSize of memory to allocate + * @returns NULL on error, new memory pointer on success. + * + * Example: + * @code + * { + * return internal_parc_realloc(ptr, 2048); + * } + * @endcode + * + */ +void *internal_parc_realloc(void *pointer, size_t newSize); + +/** + * Map free() method call to a PARC internal memory method + * + * @param [in] pointer to memory to free + * + * Example: + * @code + * { + * internal_parc_free(ptr); + * } + * @endcode + * + */ +void internal_parc_free(void *ptr); + +/** + * Verify and initialize libevent + * + * Example: + * @code + * { + * internal_parc_initializeLibevent(); + * } + * @endcode + * + */ +void internal_parc_initializeLibevent(void); + +/** + * Convert from/to libevent dispatcher options and PARCEventSchedulerDispatcherType options. + * + * Example: + * @code + * { + * PARCEventSchedulerDispatchType type = internal_eventloop_options_to_PARCEventSchedulerDispatchType(EVLOOP_ONCE); + * short evtype = internal_PARCEventSchedulerDispatchType_to_eventloop_options(PARCEventSchedulerDispatchType_LoopOnce); + * } + * @endcode + * + */ +PARCEventSchedulerDispatchType internal_eventloop_options_to_PARCEventSchedulerDispatchType(short evoptions); +short internal_PARCEventSchedulerDispatchType_to_eventloop_options(PARCEventSchedulerDispatchType options); + +/** + * Convert from/to libevent bufferevent options and PARCEventQueueOption. + * + * Example: + * @code + * { + * PARCEventQueueOption parcEventQueueOption = internal_bufferevent_options_to_PARCEventQueueOption(BEV_OPT_CLOSE_ON_FREE); + * short buffereventOption = internal_PARCEventQueueOption_to_bufferevent_options(PARCEventQueueOption_CloseOnFree); + * } + * @endcode + * + */ +PARCEventQueueOption internal_bufferevent_options_to_PARCEventQueueOption(short evflags); +short internal_PARCEventQueueOption_to_bufferevent_options(PARCEventQueueOption flags); + +/** + * Convert from/to libevent bufferevent types and PARCEventQueueEventType. + * + * Example: + * @code + * { + * PARCEventQueueEventType parcEventQueueEventType = internal_bufferevent_type_to_PARCEventQueueEventType(BEV_EVENT_READING); + * short buffereventOptions = internal_PARCEventQueueEventType_to_bufferevent_type(PARCEventQueueEventType_Reading); + * } + * @endcode + * + */ +PARCEventQueueEventType internal_bufferevent_type_to_PARCEventQueueEventType(short evtypes); +short internal_PARCEventQueueEventType_to_bufferevent_type(PARCEventQueueEventType types); + +/** + * Convert from/to libevent event types and PARCEventType. + * + * Example: + * @code + * { + * PARCEventType parcEventType = internal_libevent_type_to_PARCEventType(EV_READ); + * short buffereventOptions = internal_PARCEventType_to_libevent_type(PARCEventType_Read); + * } + * @endcode + * + */ +PARCEventType internal_libevent_type_to_PARCEventType(short evtypes); +short internal_PARCEventType_to_libevent_type(PARCEventType types); + +/** + * Convert from/to libevent priority types and PARCEventPriority. + * + * Example: + * @code + * { + * PARCEventPriority parcEventPriority = internal_libevent_priority_to_PARCEventPriority(0); + * short priority = internal_PARCEventPriority_to_libevent_priority(PARCEventPriority_Normal); + * } + * @endcode + * + */ +short internal_PARCEventPriority_to_libevent_priority(PARCEventPriority priority); +PARCEventPriority internal_libevent_priority_to_PARCEventPriority(short evpriority); +#endif // libparc_internal_parc_Event_h diff --git a/libparc/parc/algol/parc_ArrayList.c b/libparc/parc/algol/parc_ArrayList.c new file mode 100644 index 00000000..e9597070 --- /dev/null +++ b/libparc/parc/algol/parc_ArrayList.c @@ -0,0 +1,381 @@ +/* + * 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 <stdio.h> + +#include <stdbool.h> +#include <string.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_ArrayList.h> +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_Memory.h> + +PARCListInterface *PARCArrayListAsPARCList = &(PARCListInterface) { + .Add = (bool (*)(void *, void *))parcArrayList_Add, + .AddAtIndex = (void (*)(void *, int index, void *))parcArrayList_InsertAtIndex, + .AddCollection = (bool (*)(void *, PARCCollection *))NULL, + .AddCollectionAtIndex = (bool (*)(void *, int index, PARCCollection *))NULL, + .Clear = (void (*)(void *))parcArrayList_Clear, + .Contains = (bool (*)(const void *, const PARCObject *))NULL, + .ContainsCollection = (bool (*)(const void *, const PARCCollection *))NULL, + .Copy = (void * (*)(const PARCList *))parcArrayList_Copy, + .Destroy = (void (*)(void **))parcArrayList_Destroy, + .Equals = (bool (*)(const void *, const void *))parcArrayList_Equals, + .GetAtIndex = (void * (*)(const void *, size_t))parcArrayList_Get, + .HashCode = (PARCHashCode (*)(const void *))NULL, + .IndexOf = (size_t (*)(const void *, const PARCObject *element))NULL, + .IsEmpty = (bool (*)(const void *))parcArrayList_IsEmpty, + .LastIndexOf = (size_t (*)(void *, const PARCObject *element))NULL, + .Remove = (bool (*)(void *, const PARCObject *))NULL, + .RemoveAtIndex = (void * (*)(PARCList *, size_t))parcArrayList_RemoveAtIndex, + .RemoveCollection = (bool (*)(void *, const PARCCollection *))NULL, + .RetainCollection = (bool (*)(void *, const PARCCollection *))NULL, + .SetAtIndex = (void * (*)(void *, size_t index, void *))parcArrayList_Set, + .Size = (size_t (*)(const void *))parcArrayList_Size, + .SubList = (PARCList * (*)(const void *, size_t, size_t))NULL, + .ToArray = (void** (*)(const void *))NULL, +}; + +struct parc_array_list { + void **array; + size_t numberOfElements; + size_t limit; + bool (*equalsElement)(void *x, void *y); + void (*destroyElement)(void **elementAddress); +}; + +static size_t +_remaining(const PARCArrayList *array) +{ + return array->limit - array->numberOfElements; +} + +static PARCArrayList * +_ensureCapacity(PARCArrayList *array, size_t newCapacity) +{ + void *newArray = parcMemory_Reallocate(array->array, newCapacity * sizeof(void *)); + + if (newArray == NULL) { + return NULL; + } + array->array = newArray; + array->limit = newCapacity; + + return array; +} + +static PARCArrayList * +_ensureRemaining(PARCArrayList *array, size_t remnant) +{ + assertNotNull(array, "Parameter must be a non-null PARCArrayList pointer."); + + if (_remaining(array) < remnant) { + size_t newCapacity = parcArrayList_Size(array) + remnant; + return _ensureCapacity(array, newCapacity); + } + return array; +} + +bool +parcArrayList_IsValid(const PARCArrayList *instance) +{ + bool result = false; + + if (instance != NULL) { + if (instance->numberOfElements == 0 ? true : instance->array != NULL) { + result = true; + } + } + + return result; +} + +void +parcArrayList_AssertValid(const PARCArrayList *instance) +{ + trapIllegalValueIf(instance == NULL, "Parameter must be a non-null PARC_ArrayList pointer."); + assertTrue(instance->numberOfElements == 0 ? true : instance->array != NULL, "PARC_ArrayList size is inconsistent."); +} + +static PARCArrayList * +_parcArrayList_Init(PARCArrayList *arrayList, + size_t nElements, + size_t limit, + void **array, + bool (*equalsElement)(void *x, void *y), + void (*destroyElement)(void **elementAddress)) +{ + if (arrayList != NULL) { + arrayList->array = array; + arrayList->numberOfElements = nElements; + arrayList->limit = limit; + arrayList->equalsElement = equalsElement; + arrayList->destroyElement = destroyElement; + } + return arrayList; +} + +PARCArrayList * +parcArrayList_Create(void (*destroyElement)(void **element)) +{ + PARCArrayList *result = parcMemory_AllocateAndClear(sizeof(PARCArrayList)); + assertNotNull(result, "Memory allocation of PARCArrayList failed"); + + return _parcArrayList_Init(result, 0, 0, NULL, NULL, destroyElement); +} + +PARCArrayList * +parcArrayList_Create_Capacity(bool (*equalsElement)(void *x, void *y), void (*destroyElement)(void **element), size_t size) +{ + PARCArrayList *result = parcMemory_AllocateAndClear(sizeof(PARCArrayList)); + assertNotNull(result, "Memory allocation of PARCArrayList failed"); + + _parcArrayList_Init(result, 0, 0, NULL, equalsElement, destroyElement); + + if (result != NULL) { + _ensureRemaining(result, size); + } + + return result; +} + +bool +parcArrayList_Add(PARCArrayList *array, const void *pointer) +{ + parcArrayList_OptionalAssertValid(array); + + if (_ensureRemaining(array, 1) == NULL) { + trapOutOfMemory("Cannot increase space for PARCArrayList."); + } + array->array[array->numberOfElements++] = (void *) pointer; + + return true; +} + +PARCArrayList * +parcArrayList_AddAll(PARCArrayList *array, void *argv[], size_t argc) +{ + for (size_t i = 0; i < argc; i++) { + parcArrayList_Add(array, argv[i]); + } + + return array; +} + +bool +parcArrayList_IsEmpty(const PARCArrayList *list) +{ + parcArrayList_OptionalAssertValid(list); + + return list->numberOfElements == 0; +} + +bool +parcArrayList_Equals(const PARCArrayList *a, const PARCArrayList *b) +{ + bool result = true; + + if (a != b) { + if (a == NULL || b == NULL) { + result = false; + } else if (a->numberOfElements != b->numberOfElements) { + result = false; + } else { + for (size_t i = 0; i < a->numberOfElements; i++) { + if (a->equalsElement != NULL) { + if (!a->equalsElement(a->array[i], b->array[i])) { + result = false; + break; + } + } else { + if (a->array[i] != b->array[i]) { + result = false; + break; + } + } + } + } + } + + return result; +} + +void * +parcArrayList_RemoveAtIndex(PARCArrayList *array, size_t index) +{ + trapOutOfBoundsIf(index >= array->numberOfElements, "Index must be within the range [0, %zu)", array->numberOfElements); + + void *element = array->array[index]; + + // Adjust the list to elide the element. + for (size_t i = index; i < array->numberOfElements - 1; i++) { + array->array[i] = array->array[i + 1]; + } + array->numberOfElements--; + + return element; +} + +void +parcArrayList_Set(const PARCArrayList *array, size_t index, void *pointer) +{ + trapOutOfBoundsIf(index >= array->numberOfElements, "Index must be within the range [0, %zu)", array->numberOfElements); + + array->array[index] = pointer; +} + +void * +parcArrayList_Get(const PARCArrayList *array, size_t index) +{ + trapOutOfBoundsIf(index >= array->numberOfElements, "Index must be within the range [0, %zu)", array->numberOfElements); + + return array->array[index]; +} + +void * +parcArrayList_Peek(const PARCArrayList *list) +{ + return parcArrayList_Get(list, parcArrayList_Size(list) - 1); +} + +size_t +parcArrayList_Size(const PARCArrayList *pointerArray) +{ + return pointerArray->numberOfElements; +} + +void +parcArrayList_Destroy(PARCArrayList **arrayPtr) +{ + assertNotNull(arrayPtr, "Parameter must be a non-null pointer to a PARC_ArrayList pointer."); + + PARCArrayList *array = *arrayPtr; + + parcArrayList_OptionalAssertValid(array); + + assertTrue(array->numberOfElements == 0 ? true : array->array != NULL, "PARC_ArrayList is inconsistent."); + + if (array->destroyElement != NULL) { + for (size_t i = 0; i < array->numberOfElements; i++) { + if (array->array[i] != NULL) { + array->destroyElement(&array->array[i]); + } + } + } + + if (array->array != NULL) { + parcMemory_Deallocate((void **) &(array->array)); + } + + parcMemory_Deallocate((void **) &array); + *arrayPtr = 0; +} + +PARCArrayList * +parcArrayList_Copy(const PARCArrayList *original) +{ + parcArrayList_OptionalAssertValid(original); + + PARCArrayList *result = parcArrayList_Create(original->destroyElement); + + if (result != NULL) { + for (size_t i = 0; i < original->numberOfElements; i++) { + parcArrayList_Add(result, original->array[i]); + } + } + + return result; +} + +void +parcArrayList_StdlibFreeFunction(void **elementPtr) +{ + if (elementPtr != NULL) { + free(*elementPtr); + *elementPtr = 0; + } +} + +void +parcArrayList_PARCMemoryFreeFunction(void **elementPtr) +{ + if (elementPtr != NULL) { + parcMemory_Deallocate((void **) elementPtr); + *elementPtr = 0; + } +} + +PARCArrayList * +parcArrayList_RemoveAndDestroyAtIndex(PARCArrayList *array, size_t index) +{ + parcArrayList_OptionalAssertValid(array); + + assertTrue(index < array->numberOfElements, "Index must be ( 0 <= index < %zd). Actual=%zd", array->numberOfElements, index); + + if (index < array->numberOfElements) { + // Destroy the element at the given index. + if (array->destroyElement != NULL) { + array->destroyElement(&array->array[index]); + } + + // Adjust the list to elide the element. + for (size_t i = index; i < array->numberOfElements - 1; i++) { + array->array[i] = array->array[i + 1]; + } + array->numberOfElements--; + } + + return array; +} + +PARCArrayList * +parcArrayList_InsertAtIndex(PARCArrayList *array, size_t index, const void *pointer) +{ + parcArrayList_OptionalAssertValid(array); + size_t length = parcArrayList_Size(array); + + assertTrue(index <= array->numberOfElements, "You can't insert beyond the end of the list"); + + // Create space and grow the array if needed + _ensureRemaining(array, length + 1); + for (size_t i = index; i < length; i++) { + array->array[i + 1] = array->array[i]; + } + array->numberOfElements++; + + array->array[index] = (void *) pointer; + + return array; +} + +void * +parcArrayList_Pop(PARCArrayList *array) +{ + return parcArrayList_RemoveAtIndex(array, parcArrayList_Size(array) - 1); +} + +void +parcArrayList_Clear(PARCArrayList *array) +{ + while (!parcArrayList_IsEmpty(array)) { + parcArrayList_RemoveAndDestroyAtIndex(array, parcArrayList_Size(array) - 1); + } +} diff --git a/libparc/parc/algol/parc_ArrayList.h b/libparc/parc/algol/parc_ArrayList.h new file mode 100644 index 00000000..1e2be6b0 --- /dev/null +++ b/libparc/parc/algol/parc_ArrayList.h @@ -0,0 +1,577 @@ +/* + * 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. + */ + +/** + * @file parc_ArrayList.h + * @ingroup datastructures + * @brief A dynamic array of void * pointers. + * + * This module implements a dynamic array of simple void pointers. + * Users can create an empty array, or preprovisioned with an initial capacity of N elements. + * + */ +#ifndef libparc_parc_ArrayList_h +#define libparc_parc_ArrayList_h + +#include <stdint.h> +#include <stdbool.h> + +#include <parc/algol/parc_List.h> +#include <parc/algol/parc_Iterator.h> + +#ifdef PARCLibrary_DISABLE_VALIDATION +# define parcArrayList_OptionalAssertValid(_instance_) +#else +# define parcArrayList_OptionalAssertValid(_instance_) parcArrayList_AssertValid(_instance_) +#endif + +struct parc_array_list; +typedef struct parc_array_list PARCArrayList; + +/** + * The mapping of a `PARCArrayList` to the generic `PARCList`. + */ +extern PARCListInterface *PARCArrayListAsPARCList; + +/** + * Determine if a `PARCArrayList` is valid. + * + * @param [in] instance A pointer to a PARCArrayList instance. + * + * @return true The instance is valid. + * @return true The instance is invalid. + * + * Example: + * @code + * { + * PARCArrayList *list = parcArrayList_Create(NULL); + * if (parcArrayList_IsValid(list)) { + * printf("The list is valid."); + * } + * parcArrayList_Destroy((list); + * @endcode + */ +bool parcArrayList_IsValid(const PARCArrayList *instance); + +/** + * Assert that a `PARCArrayList` is valid. + * + * @param [in] instance A pointer to a PARCArrayList instance. + * + * Example: + * @code + * { + * PARCArrayList *list = parcArrayList_Create(NULL); + * + * parcArrayList_AssertValid(list); + * + * parcArrayList_Destroy((list); + * @endcode + */ +void parcArrayList_AssertValid(const PARCArrayList *instance); + +/** + * Add an element to the given PARCArrayList. + * + * If the PARCArrayList was constructed with a destroyer, + * the pointer will be destroyed when element is removed or the list is destroyed. + * + * @param [in] array A pointer to a `PARCArrayList`. + * @param [in] pointer A void pointer to the item to be added to the list. + * + * @return True If the value was successfully added to the list. + * @return False Otherwise + * + * Example: + * @code + * { + * PARCArrayList *array = parcArrayList_Create(parcArrayList_StdlibFreeFunction); + * void *elements[] = { + * strdup("a"), + * strdup("b"), + * strdup("c"), + * }; + * + * bool wasAdded = parcArrayList_Add(array, elements); + * printf("Successfully added the base pointer? %d\n", wasAdded); + * + * ... + * + * parcArrayList_Destroy(&array); + * } + * @endcode + */ +bool parcArrayList_Add(PARCArrayList *array, const void *pointer); + +/** + * Add all of the pointers in the given array of pointers to the `PARCArrayList`. + * + * This is synonymous with calling {@link parcArrayList_Add()} multiple times + * + * @param [in] array A pointer to `PARCArrayList`. + * @param [in] argv A pointer to the base list of pointers. + * @param [in] argc The number of items to add to the list, starting from the abse void pointer. + * + * @return A pointer to the modified PARCArrayList. + * + * Example: + * @code + * { + * PARCArrayList *array = parcArrayList_Create(parcArrayList_StdlibFreeFunction); + * void *elements[] = { + * strdup("a"), + * strdup("b"), + * strdup("c"), + * }; + * + * parcArrayList_AddAll(array, elements, 3); + * + * ... + * + * parcArrayList_Destroy(&array); + * } + * @endcode + */ +PARCArrayList *parcArrayList_AddAll(PARCArrayList *array, void **argv, size_t argc); + +/** + * Remove an element at a specific index from a `PARCArrayList`. + * + * The element is destroyed via the function provided when calling {@link parcArrayList_Create()}. + * The index must be 0 <= index < length. + * + * @param [in,out] array A pointer to `PARCArrayList`. + * @param [in] index The index of the element to remove and destroy. + * + * @return A pointer to the modified `PARCArrayList`. + * + * Example: + * @code + * { + * PARCArrayList *array = parcArrayList_Create(parcArrayList_StdlibFreeFunction); + * void *elements[] = { + * strdup("a"), + * strdup("b"), + * strdup("c"), + * }; + * + * parcArrayList_AddAll(array, elements, 3); + * parcArrayList_RemoveAndDestroyAtIndex(array, 0); + * + * size_t size = parcArrayList_Size(array); + * // size will now be three + * + * ... + * + * parcArrayList_Destroy(&array); + * } + * @endcode + */ +PARCArrayList *parcArrayList_RemoveAndDestroyAtIndex(PARCArrayList *array, size_t index); + +/** + * Return the element at index. Remove the element from the array. + * + * The index must be 0 <= index < length. + * + * @param [in,out] array A pointer to a `PARCArrayList` instance. + * @param [in] index The index of the element to remove. + * + * @return void* A pointer (void *) to the element that was removed. + * + * Example: + * @code + * { + * PARCArrayList *array = parcArrayList_Create(parcArrayList_StdlibFreeFunction); + * void *elements[] = { + * strdup("a"), + * strdup("b"), + * strdup("c"), + * }; + * + * parcArrayList_AddAll(array, elements, 3); + * void *firstString = parcArrayList_RemoveAtIndex(array, 0); + * + * printf("First string: %s\n", firstString); + * + * ... + * + * parcArrayList_Destroy(&array); + * } + * @endcode + */ +void *parcArrayList_RemoveAtIndex(PARCArrayList *array, size_t index); + +/** + * Insert an element at the index location. Elements will be moved up if required. + * + * You may not insert beyond the list end. (You can insert at the end, but not any more). + * + * @param [in,out] array A `PARCArrayList` + * @param [in] pointer A pointer to the element to insert + * @param [in] index An index in the `PARCArrayList` + * @return A pointer to the modified `PARCArrayList` + * + * Example: + * @code + * { + * PARCArrayList *array = parcArrayList_Create(parcArrayList_StdlibFreeFunction); + * + * parcArrayList_InsertAtIndex(array, strdup("a"), 0); + * void *firstString = parcArrayList_RemoveAtIndex(array, 0); + * printf("First string is 'a': %s\n", firstString); + * + * ... + * + * parcArrayList_Destroy(&array); + * } + * @endcode + */ +PARCArrayList *parcArrayList_InsertAtIndex(PARCArrayList *array, size_t index, const void *pointer); + +/** + * Create an instance of an empty `PARCArrayList`. + * + * @param [in] destroyElement + * A pointer to a function that will destroy (or equivalent) the element pointed to by `element` + * + * @return A pointer to the new `PARCArrayList` instance, or NULL if no memory could be allocated. + * + * Example: + * @code + * { + * PARCArrayList *array = parcArrayList_Create(parcArrayList_StdlibFreeFunction); + * ... + * } + * @endcode + */ +PARCArrayList *parcArrayList_Create(void (*destroyElement)(void **elementAddress)); + +/** + * Create an instance of a PARCArrayList pre-provisioned to contain the specified number of elements. + * + * @param [in] equalsElement + * A pointer to a function that will determine equality between two elements in the array. + * @param [in] destroyElement + * A pointer to a function that will destroy (or equivalent) the element pointed to by <code>element</code> + * @param [in] size + * The number of initial elements to provision for. + * @return A pointer to a new `PARCArrayList` instance, or NULL if no memory could be allocated. + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCArrayList *parcArrayList_Create_Capacity(bool (*equalsElement)(void *x, void *y), void (*destroyElement)(void **elementAddress), size_t size); + +/** + * Copy a `PARCArrayList` instance. + * + * Create a new `PARCArrayList` instance with the same structure and content as the original. + * + * @param [in] array A pointer to a `PARCArrayList` instance to copy. + * + * @return A pointer to a new `PARCArrayList` instance with a copy of the original, or NULL if no memory could be allocated. + * + * Example: + * @code + * { + * PARCArrayList *array = parcArrayList_Create(parcArrayList_StdlibFreeFunction); + * parcArrayList_InsertAtIndex(array, strdup("a"), 0); + * + * PARCArrayList *sameList = parcArrayList_Copy(array); + * + * if (parcArrayList_Equals(array, sameList)) { + * // true + * } else { + * // false + * } + * + * parcArrayList_Destroy(&array); + * } + * @endcode + */ +PARCArrayList *parcArrayList_Copy(const PARCArrayList *array); + +/** + * Destroy a `PARCArrayList` instance. + * + * Destroy the given `PARCArrayList` by freeing all memory used by it. + * + * @param [in,out] arrayPtr A pointer to the pointer of the `PARCArrayList` to be destroyed. + * + * Example: + * @code + * { + * PARCArrayList *array = parcArrayList_Create(parcArrayList_StdlibFreeFunction); + * + * ... + * + * parcArrayList_Destroy(&array); + * } + * @endcode + */ +void parcArrayList_Destroy(PARCArrayList **arrayPtr); + +/** + * Set an element from the given `PARCArrayList` at a specified index to a value. + * + * The index must be 0 <= index < length. + * + * @param [in] array A pointer to a `PARCArrayList` instance. + * @param [in] index The index into the `PARCArrayList` instance. + * @param [in] pointer A void pointer to the item to be assigned to the index + * + * Example: + * @code + * { + * PARCArrayList *array = parcArrayList_Create(parcArrayList_StdlibFreeFunction); + * void *elements[] = { + * strdup("a"), + * strdup("b"), + * strdup("c"), + * }; + * + * bool wasAdded = parcArrayList_Add(array, elements); + * // how to free the pointer at index 1 without deallocating the slot? XXX + * parcArrayList_Set(array, 1, strdup("d"); + * parcArrayList_Destroy(&array); + * } + * @endcode + */ +void parcArrayList_Set(const PARCArrayList *array, size_t index, void *pointer); + +/** + * Get an element from the given `PARCArrayList` at a specified index. + * + * The index must be 0 <= index < length. + * + * @param [in] array A pointer to a `PARCArrayList` instance. + * @param [in] index The index into the `PARCArrayList` instance. + * + * @return A pointer (void *) to the element in the list. + * + * Example: + * @code + * { + * PARCArrayList *array = parcArrayList_Create(parcArrayList_StdlibFreeFunction); + * void *elements[] = { + * strdup("a"), + * strdup("b"), + * strdup("c"), + * }; + * + * parcArrayList_AddAll(array, elements, 3); + * void *firstString = parcArrayList_Get(array, 0); + * + * printf("First string: %s\n", firstString); + * + * ... + * + * parcArrayList_Destroy(&array); + * } + * @endcode + */ +void *parcArrayList_Get(const PARCArrayList *array, size_t index); + +/** + * Return the number of elements in the given `PARCArrayList`. + * + * The size is the number of elements, NOT the size in bytes of all elements maintained in the list. + * + * @param [in] array A pointer to a `PARCArrayList` instance. + * + * @return A size_t of the number of elements in the given `PARCArrayList`. + * + * Example: + * @code + * { + * PARCArrayList *array = parcArrayList_Create(parcArrayList_StdlibFreeFunction); + * void *elements[] = { + * strdup("a"), + * strdup("b"), + * strdup("c"), + * }; + * + * parcArrayList_AddAll(array, elements, 3); + * + * size_t size = parcArrayList_Size(array); + * printf("Size =?= 3: %d\n", size == 3); + * + * parcArrayList_Destroy(&array); + * } + * @endcode + */ +size_t parcArrayList_Size(const PARCArrayList *array); + +/** + * Determine if two `PARCArrayList` instances are equal. + * + * The following equivalence relations on non-null `PARCArrayList` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, parcArrayList_Equals(x, x) must return true. + * + * * It is symmetric: for any non-null reference values x and y, parcArrayList_Equals(x, y) must return true if and only if + * parcArrayList_Equals(y x) returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * parcArrayList_Equals(x, y) returns true and + * parcArrayList_Equals(y, z) returns true, + * then parcArrayList_Equals(x, z) must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of parcArrayList_Equals(x, y) + * consistently return true or consistently return false. + * + * * For any non-null reference value x, parcArrayList_Equals(x, NULL)) must return false. + * + * @param [in] a The first `PARCArrayList` instance. + * @param [in] b The second `PARCArrayList` instance. + * + * @return true the given PARCArrayList instances are equal. + * @return false the given PARCArrayList instances are not equal. + * + * Example: + * @code + * { + * PARCArrayList *a = parcArrayList_Create(); + * PARCArrayList *b = parcArrayList_Create(); + * + * if (parcArrayList_Equals(a, b)) { + * // true + * } else { + * // false + * } + * } + * @endcode + */ +bool parcArrayList_Equals(const PARCArrayList *a, const PARCArrayList *b); + +/** + * Standard library free(3) wrapper for use as a destructor function for elements of a `PARCArrayList`. + * + * The create functions for `PARCArrayList` have an input parameter that is a pointer to a function that + * will be called for each element when the `PARCArrayList` is destroyed, and when an element is removed via + * {@link parcArrayList_RemoveAndDestroyAtIndex}. + * This destroy function has a different calling signature than the standard library's free(3) function. + * This function is a wrapper providing the correct facade for the standard library free(3) function. + * + * @param [in,out] element A pointer to the element to be destroyed. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void parcArrayList_StdlibFreeFunction(void **element); + +/** + * PARC_Memory free() wrapper for use as a destructor function for elements of a `PARCArrayList`. + * + * The create functions for `PARCArrayList` have an input parameter that is a pointer to a function that + * will be called for each element when the `PARCArrayList` is destroyed, and when an element is removed via + * {@link parcArrayList_RemoveAndDestroyAtIndex}. + * This destroy function has a different calling signature than the PARC_Memory's free(3) function. + * This function is a wrapper providing the correct facade for the PARC_Memory's free(3) function. + * + * @param [in,out] elementPtr A pointer to the element to be destroyed. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void parcArrayList_PARCMemoryFreeFunction(void **elementPtr); + +/** + * Tests if this list is empty. + * + * + * @param [in] list A pointer to the `PARCArrayList` to test. + * @return true if the stack is empty. + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool parcArrayList_IsEmpty(const PARCArrayList *list); + +/** + * Returns a pointer to object at the top of this stack without removing it from the stack. + * + * @param [in] list A pointer to `PARCArrayList`. + * @return A pointer to the first element of the `PARCArrayList`. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void *parcArrayList_Peek(const PARCArrayList *list); + +/** + * Removes the object at the top of this stack and returns that object as the value of this function. + * + * @param [in,out] list A pointer to the `PARCArrayList`. + * @return A pointer to what was the first element of the `PARCArrayList`. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void *parcArrayList_Pop(PARCArrayList *list); + +/** + * Removes all objects from this stack. + * + * @param [in,out] list A pointer to the `PARCArrayList`. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void parcArrayList_Clear(PARCArrayList *list); + +/** + * Pushes an item onto the top of this stack. + * + * @param [in,out] list A pointer to a `PARCArrayList`. + * @param [in] item A pointer to an element to be added to the `PARCArrayList`. + * @return A pointer to the item just pushed on to the `PARCArrayLis. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void *parcArrayList_Push(PARCArrayList *list, void *item); + +/** + * Returns the 1-based position where an object is on this stack. + * + * @param [in] list A pointer to `PARCArrayList`. + * @param [in] element A pointer to the element to locate in the `PARCArrayList`. + * @return int The index of the located element in the `PARCArrayList` + * + * Example: + * @code + * <#example#> + * @endcode + */ +int parcArrayList_Search(PARCArrayList *list, void *element); +#endif // libparc_parc_ArrayList_h diff --git a/libparc/parc/algol/parc_AtomicInteger.c b/libparc/parc/algol/parc_AtomicInteger.c new file mode 100755 index 00000000..b32a698f --- /dev/null +++ b/libparc/parc/algol/parc_AtomicInteger.c @@ -0,0 +1,94 @@ +/* + * 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 <pthread.h> + +#include <parc/algol/parc_AtomicInteger.h> + +#ifdef USE_GCC_EXTENSIONS +uint32_t +parcAtomicInteger_Uint32IncrementGCC(uint32_t *value) +{ + return __sync_add_and_fetch(value, 1); +} + +uint32_t +parcAtomicInteger_Uint32DecrementGCC(uint32_t *value) +{ + return __sync_sub_and_fetch(value, 1); +} + +uint64_t +parcAtomicInteger_Uint64IncrementGCC(uint64_t *value) +{ + return __sync_add_and_fetch(value, 1); +} + +uint64_t +parcAtomicInteger_Uint64DecrementGCC(uint64_t *value) +{ + return __sync_sub_and_fetch(value, 1); +} +#endif + + +// Since there is no per integer mutex, we must use something to protect an integer from multiple, +// simultaneous competitors. +// Unfortunately, this is an indiscrimiate mutex causing all atomic integer updates to be +// serialized rather than each individually protected. +static pthread_mutex_t _parcAtomicInteger_PthreadMutex = PTHREAD_MUTEX_INITIALIZER; + +uint32_t +parcAtomicInteger_Uint32IncrementPthread(uint32_t *value) +{ + pthread_mutex_lock(&_parcAtomicInteger_PthreadMutex); + *value = *value + 1; + uint32_t result = *value; + pthread_mutex_unlock(&_parcAtomicInteger_PthreadMutex); + return result; +} + +uint32_t +parcAtomicInteger_Uint32DecrementPthread(uint32_t *value) +{ + pthread_mutex_lock(&_parcAtomicInteger_PthreadMutex); + *value = *value - 1; + uint32_t result = *value; + pthread_mutex_unlock(&_parcAtomicInteger_PthreadMutex); + return result; +} + +uint64_t +parcAtomicInteger_Uint64IncrementPthread(uint64_t *value) +{ + pthread_mutex_lock(&_parcAtomicInteger_PthreadMutex); + *value = *value + 1; + uint64_t result = *value; + pthread_mutex_unlock(&_parcAtomicInteger_PthreadMutex); + return result; +} + +uint64_t +parcAtomicInteger_Uint64DecrementPthread(uint64_t *value) +{ + pthread_mutex_lock(&_parcAtomicInteger_PthreadMutex); + *value = *value - 1; + uint64_t result = *value; + pthread_mutex_unlock(&_parcAtomicInteger_PthreadMutex); + return result; +} diff --git a/libparc/parc/algol/parc_AtomicInteger.h b/libparc/parc/algol/parc_AtomicInteger.h new file mode 100755 index 00000000..729d9a01 --- /dev/null +++ b/libparc/parc/algol/parc_AtomicInteger.h @@ -0,0 +1,73 @@ +/* + * 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. + */ + +/** + * @file parc_AtomicInteger.h + * @ingroup threading + * @brief An integer value that may be updated automatically. + * + */ +#ifndef libparc_parc_AtomicInteger_h +#define libparc_parc_AtomicInteger_h + +#include <stdint.h> + +/* + * If we are compiling with GCC or Clang, then + * we have some compiler extensions to make use of processer + * compare-and-swap and other kinds of synchronisation primitives. + */ +#if defined(__GNUC__) || defined(__clang__) +# define USE_GCC_EXTENSIONS 1 +#endif + +// Turn this off until the problem on the SMP machines is worked out, case 787. +#undef USE_GCC_EXTENSIONS + +#ifdef USE_GCC_EXTENSIONS +uint32_t parcAtomicInteger_Uint32IncrementGCC(uint32_t *value); + +uint32_t parcAtomicInteger_Uint32DecrementGCC(uint32_t *value); + +uint64_t parcAtomicInteger_Uint64IncrementGCC(uint64_t *value); + +uint64_t parcAtomicInteger_Uint64DecrementGCC(uint64_t *value); + +#define parcAtomicInteger_Uint32Increment parcAtomicInteger_Uint32IncrementGCC + +#define parcAtomicInteger_Uint32Decrement parcAtomicInteger_Uint32DecrementGCC + +#define parcAtomicInteger_Uint64Increment parcAtomicInteger_Uint64IncrementGCC + +#define parcAtomicInteger_Uint64Decrement parcAtomicInteger_Uint64DecrementGCC + +#else +uint32_t parcAtomicInteger_Uint32IncrementPthread(uint32_t *value); + +uint32_t parcAtomicInteger_Uint32DecrementPthread(uint32_t *value); + +uint64_t parcAtomicInteger_Uint64IncrementPthread(uint64_t *value); + +uint64_t parcAtomicInteger_Uint64DecrementPthread(uint64_t *value); + +#define parcAtomicInteger_Uint32Increment parcAtomicInteger_Uint32IncrementPthread + +#define parcAtomicInteger_Uint32Decrement parcAtomicInteger_Uint32DecrementPthread + +#define parcAtomicInteger_Uint64Increment parcAtomicInteger_Uint64IncrementPthread + +#define parcAtomicInteger_Uint64Decrement parcAtomicInteger_Uint64DecrementPthread +#endif +#endif // libparc_parc_AtomicInteger_h diff --git a/libparc/parc/algol/parc_Base64.c b/libparc/parc/algol/parc_Base64.c new file mode 100755 index 00000000..38020479 --- /dev/null +++ b/libparc/parc/algol/parc_Base64.c @@ -0,0 +1,290 @@ +/* + * 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. + */ + +/** + * + * Encode/decode base64. Encoding goes to one long line, no line breaks. + * Decoding will accept CRLF linebreaks in the data and skip them. + * + * Following the language of RFC 4648, encoding proceeds in a "quantum" of + * 3 bytes of plaintext to 4 bytes of encoded data. Decoding goes in + * a 4-byte quantum to 3-byte decoded data. + * + * If decoding fails (e.g. there's a non-base64 character), then the output + * buffer is rewound to the starting position and a failure is indicated. + * + * Decoding using a 256 byte table. Each byte of the 4-byte quantum is looked + * up and if its a valid character -- it resolves to a value 0..63, then that + * value is shifted to the right position in the output. Values CR and LF have + * the special token "_" in the table, which means "skip". That token has value + * ascii value 95, is we can detect it as outside base64. Similarly, all the + * invalid characters have the symbol "~", which is ascii 127. + * + */ + +#include <config.h> + +#include <stdio.h> +#include <string.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_Base64.h> +#include <parc/algol/parc_Memory.h> + +const uint8_t base64code[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +const uint8_t pad = '='; + +const uint8_t invalid = '~'; // has ascii value 127, outside base64 +const uint8_t skip = '_'; // has ascii value 95, outside the base64 values + +// an 256-entry table to lookup decode values. If the value is "invalid", then it's not +// a base64 character. +const uint8_t decodeTable[256] = { +/* 0 */ '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '_', '~', '~', '_', '~', '~', +/* 16 */ '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', +/* 32 */ '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', 62, '~', '~', '~', 63, +/* 48 */ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, '~', '~', '~', '~', '~', '~', +/* 64 */ '~', 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, +/* 80 */ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, '~', '~', '~', '~', '~', +/* 96 */ '~', 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, +/* 112 */ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, '~', '~', '~', '~', '~', +/* 128 */ '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', + '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', + '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', + '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', + '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', + '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', + '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', + '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~' +}; + +#define min(a, b) ((a < b) ? a : b) + +/** + * Encode the 3-byte quantum pointed to by <code>quantum</code> into 4 encoded characters. + * It includes `padLength` of pad necessary at the end. + */ +static void +_encodeWithPad(PARCBufferComposer *output, const uint8_t *quantum, size_t padLength) +{ + assertTrue(padLength < 3, "Degenerate case -- should never pad all 3 bytes!"); + + unsigned index; + + uint8_t paddedQuantum[] = { 0, 0, 0 }; + memcpy(paddedQuantum, quantum, 3 - padLength); + + for (index = 0; index < 4; index++) { + if (index + padLength < 4) { + /* + * The four base64 symbols fall in to these locations in the + * 3-byte input + * + * aaaaaabb | bbbbcccc | ccdddddd + * + * This switch statement, based on the "a" "b" "c" or "d" case + * extracts the corresponding 6 bits from its location in the + * byte aray. + */ + int sixbit = 0; + switch (index) { + case 0: // aaaaaa + sixbit = paddedQuantum[0] >> 2; + break; + + case 1: // bbbbbb + sixbit = ((paddedQuantum[0] & 0x03) << 4) | (paddedQuantum[1] >> 4); + break; + + case 2: // cccccc + sixbit = ((paddedQuantum[1] & 0x0F) << 2) | (paddedQuantum[2] >> 6); + break; + + case 3: // dddddd + sixbit = paddedQuantum[2] & 0x3F; + break; + } + uint8_t encodedChar = base64code[ sixbit ]; + parcBufferComposer_PutUint8(output, encodedChar); + } else { + parcBufferComposer_PutUint8(output, pad); + } + } +} + +/** + * Decode the 4-byte quantum of base64 to binary. + */ +static bool +_decode(PARCBufferComposer *output, uint8_t *quantum) +{ + uint8_t threebytes[3] = { 0, 0, 0 }; + size_t length_to_append = 0; + + for (int index = 0; index < 4; index++) { + uint8_t c = quantum[index]; + if (c != pad) { + uint8_t value = decodeTable[c]; + + // if its a non-base64 character, bail out of here + if (value == invalid) { + return false; + } + + /* + * The four base64 symbols fall in to these locations in the + * final 3-byte output + * + * aaaaaabb | bbbbcccc | ccdddddd + */ + switch (index) { + case 0: // aaaaaa + threebytes[0] |= value << 2; + break; + + case 1: // bbbbbb + threebytes[0] |= (value & 0x30) >> 4; + threebytes[1] |= (value & 0x0F) << 4; + + // we've finished threebytes[0] + length_to_append = 1; + break; + + case 2: // cccccc + threebytes[1] |= value >> 2; + threebytes[2] |= (value & 0x03) << 6; + + // we've finished threebytes[1] + length_to_append = 2; + break; + + case 3: // dddddd + threebytes[2] |= value; + + // we've finished threebytes[2] + length_to_append = 3; + break; + } + } + } + + parcBufferComposer_PutArray(output, threebytes, length_to_append); + return true; +} + +PARCBufferComposer * +parcBase64_Encode(PARCBufferComposer *result, PARCBuffer *plainText) +{ + size_t remaining = parcBuffer_Remaining(plainText); + if (remaining > 0) { + const uint8_t *buffer = parcBuffer_Overlay(plainText, 0); + result = parcBase64_EncodeArray(result, remaining, buffer); + } + + return result; +} + +PARCBufferComposer * +parcBase64_EncodeArray(PARCBufferComposer *output, size_t length, const uint8_t array[length]) +{ + size_t offset = 0; + + // Encode 3-byte tuples + while (offset < length) { + const uint8_t *quantum = array + offset; + size_t padLength = 3 - min(3, length - offset); + _encodeWithPad(output, quantum, padLength); + offset += 3; + } + + return output; +} + +PARCBufferComposer * +parcBase64_Decode(PARCBufferComposer *output, PARCBuffer *encodedText) +{ + // We proceed in 4-byte blocks. All base-64 encoded data is a multiple of 4 bytes. + // If the length of encodedText is wrong, bail now + + size_t remaining = parcBuffer_Remaining(encodedText); + const uint8_t *buffer = parcBuffer_Overlay(encodedText, remaining); + return parcBase64_DecodeArray(output, remaining, buffer); +} + +PARCBufferComposer * +parcBase64_DecodeString(PARCBufferComposer *output, const char *encodedString) +{ + const uint8_t *buffer = (const uint8_t *) encodedString; + size_t length = strlen(encodedString); + return parcBase64_DecodeArray(output, length, buffer); +} + +PARCBufferComposer * +parcBase64_DecodeArray(PARCBufferComposer *output, size_t length, const uint8_t array[length]) +{ + size_t offset = 0; + bool success = true; + + // if we need to rollback, this is where we go + PARCBuffer *outputBuffer = parcBufferComposer_GetBuffer(output); + size_t rewind_to = parcBuffer_Position(outputBuffer); + + while (offset < length && success) { + // filter out line feeds and carrage returns + // parse the input in 4-byte quantums + size_t index = 0; + uint8_t quantum[4]; + + // reset success at the start of each loop. if we run out of input before + // we parse a full quantum, we'll fail the loop and rewind the output buffer. + success = false; + + // 4 == quantum length for decode + while (index < 4 && offset < length) { + uint8_t c = array[offset]; + uint8_t decoded = decodeTable[c]; + + if (decoded < 64 || c == pad) { + // this is an artifact from how the code was first written, so we + // pass the un-decoded character + quantum[index] = c; + index++; + offset++; + continue; + } + + if (decoded == skip) { + offset++; + continue; + } + + if (decoded == invalid) { + break; + } + } + + if (index == 4) { + success = _decode(output, quantum); + } + } + + if (!success) { + parcBuffer_SetPosition(outputBuffer, rewind_to); + return NULL; + } + + return output; +} diff --git a/libparc/parc/algol/parc_Base64.h b/libparc/parc/algol/parc_Base64.h new file mode 100644 index 00000000..7d60797a --- /dev/null +++ b/libparc/parc/algol/parc_Base64.h @@ -0,0 +1,129 @@ +/* + * 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. + */ + +/** + * @file parc_Base64.h + * @ingroup inputoutput + * @brief Encode/decode base64 + * + * Encoding goes to one long line, no line breaks. + * Decoding will accept CRLF linebreaks in the data and skip them. + * + * Following the language of RFC 4648, encoding proceeds in a "quantum" of + * 3 bytes of plaintext to 4 bytes of encoded data. Decoding goes in + * a 4-byte quantum to 3-byte decoded data. + * + * If decoding fails (e.g. there's a non-base64 character), then the output + * buffer is rewound to the starting position and a failure is indicated. + * + * Decoding using a 256 byte table. Each byte of the 4-byte quantum is looked + * up and if its a valid character -- it resolves to a value 0..63, then that + * value is shifted to the right position in the output. Values CR and LF have + * the special token "_" in the table, which means "skip". That token has value + * ascii value 95, is we can detect it as outside base64. Similarly, all the + * invalid characters have the symbol "~", which is ascii 127. + * + */ +#ifndef libparc_parc_Base64_h +#define libparc_parc_Base64_h + +#include <parc/algol/parc_Buffer.h> +#include <parc/algol/parc_BufferComposer.h> + +/** + * Encode the plaintext buffer to base64, as per RFC 4648, Section 4. + * + * The base64 encoding is appended to `output` at the current position, and `output` is returned. + * + * @param [in,out] output The instance of {@link PARCBufferComposer} to which the encoded @plainText is appended + * @param [in] plainText The text to encode and append to @p output + * + * @return A pointer to the @p output + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + * + */ +PARCBufferComposer *parcBase64_Encode(PARCBufferComposer *output, PARCBuffer *plainText); + +/** + * Encode the array to base64. + * + * @param [in,out] output The instance of {@link PARCBufferComposer} to which the encoded @p array is appended + * @param [in] length The length of the Array to be encoded + * @param array The array to be encoded and appended to @p output + * + * @return A pointer to the @p output + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCBufferComposer *parcBase64_EncodeArray(PARCBufferComposer *output, size_t length, const uint8_t *array); + +/** + * Base64 decode the @p encodedText and append the result to @p output, + * which is returned by the function. + * + * If the @p encodedText cannot be base64 decoded, + * @p output is reset to the starting position and the function returns NULL. + * + * @param [in,out] output The instance of {@link PARCBufferComposer} to which the decoded @p encodedText is appended + * @param [in] encodedText The array to be decoded and appended to @p output + * + * @return A pointer to the @p output, or NULL if the @p encodedText cannot be base64 decoded + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCBufferComposer *parcBase64_Decode(PARCBufferComposer *output, PARCBuffer *encodedText); + +/** + * Base64 decode the string, using strlen() to get the length. + * + * @param [in,out] output The instance of {@link PARCBufferComposer} to which the decoded @p encodedText is appended + * @param [in] encodedString The string to be decoded and appended to @p output + * + * @return A pointer to the @p output, or NULL if the @p encodedString cannot be base64 decoded + * Example: + * @code + * <#example#> + * @endcode + */ +PARCBufferComposer *parcBase64_DecodeString(PARCBufferComposer *output, const char *encodedString); + +/** + * Base64 decode the array. + * + * @param [in,out] output The instance of {@link PARCBufferComposer} to which the decoded @p array is appended + * @param [in] length The size of the array. + * @param [in] array The array to be decoded and appended to @p output + * + * @return A pointer to the @p output, or NULL if the @p encodedString cannot be base64 decoded + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCBufferComposer *parcBase64_DecodeArray(PARCBufferComposer *output, size_t length, const uint8_t *array); +#endif // libparc_parc_Base64_h diff --git a/libparc/parc/algol/parc_BitVector.c b/libparc/parc/algol/parc_BitVector.c new file mode 100755 index 00000000..578fd102 --- /dev/null +++ b/libparc/parc/algol/parc_BitVector.c @@ -0,0 +1,409 @@ +/* + * 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 <LongBow/runtime.h> + +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_BitVector.h> +#include <parc/algol/parc_Memory.h> + +#define BITS_PER_BYTE 8 +#define DEFAULT_BITARRAY_SIZE 1 +#define MAX_BIT_VECTOR_INDEX 8192 + +struct PARCBitVector { + // the number of bits allocated. + unsigned bitLength; + + // we track the number of "1"s set for fast computation + // <code>parcBitVector_NumberOfBitsSet()</code> + unsigned numberOfBitsSet; + + // optimize case where only one bit is set. + unsigned firstBitSet; + + // our backing memory. + uint8_t *bitArray; + + // Fill value to be used when extending a vector + int fillValue; +}; + +static void +_destroy(PARCBitVector **parcBitVector) +{ + parcMemory_Deallocate(&((*parcBitVector)->bitArray)); +} + +parcObject_ExtendPARCObject(PARCBitVector, _destroy, parcBitVector_Copy, NULL, NULL, NULL, NULL, NULL); + +parcObject_ImplementAcquire(parcBitVector, PARCBitVector); + +parcObject_ImplementRelease(parcBitVector, PARCBitVector); + +PARCBitVector * +parcBitVector_Create(void) +{ + PARCBitVector *parcBitVector = parcObject_CreateInstance(PARCBitVector); + assertNotNull(parcBitVector, "parcObject_CreateInstance returned NULL"); + + parcBitVector->bitArray = parcMemory_AllocateAndClear(DEFAULT_BITARRAY_SIZE); + assertNotNull(parcBitVector->bitArray, "parcMemory_AllocateAndClear(%d) returned NULL", DEFAULT_BITARRAY_SIZE); + parcBitVector->bitLength = DEFAULT_BITARRAY_SIZE * BITS_PER_BYTE; + parcBitVector->numberOfBitsSet = 0; + parcBitVector->firstBitSet = -1; + parcBitVector->fillValue = 0; + + return parcBitVector; +} + +PARCBitVector * +parcBitVector_Copy(const PARCBitVector *source) +{ + PARCBitVector *parcBitVector = parcObject_CreateInstance(PARCBitVector); + assertNotNull(parcBitVector, "parcObject_CreateInstance returned NULL"); + + size_t byteLength = source->bitLength / BITS_PER_BYTE; + parcBitVector->bitArray = parcMemory_Allocate(byteLength); + memcpy(parcBitVector->bitArray, source->bitArray, byteLength); + parcBitVector->bitLength = source->bitLength; + parcBitVector->numberOfBitsSet = source->numberOfBitsSet; + parcBitVector->firstBitSet = source->firstBitSet; + parcBitVector->fillValue = source->fillValue; + + return parcBitVector; +} + +bool +parcBitVector_Equals(const PARCBitVector *a, const PARCBitVector *b) +{ + bool equal = ((a->numberOfBitsSet == b->numberOfBitsSet) && + (a->firstBitSet == b->firstBitSet)); + + if (equal) { + int byteLength = a->bitLength / BITS_PER_BYTE; + if ((a->bitLength != b->bitLength) && + (b->bitLength < a->bitLength)) { + byteLength = b->bitLength / BITS_PER_BYTE; + } + equal = (memcmp(a->bitArray, b->bitArray, byteLength) == 0); + } + + return equal; +} + +static void +_parc_bit_vector_resize(PARCBitVector *parcBitVector, unsigned bit) +{ + assertNotNull(parcBitVector, "_parc_bit_vector_resize passed a NULL parcBitVector"); + assertTrue(bit < MAX_BIT_VECTOR_INDEX, "_parc_bit_vector_resize passed a bit index that's too large"); + + unsigned neededBits = bit + 1; + if (neededBits > parcBitVector->bitLength) { + int newSize = (neededBits / BITS_PER_BYTE) + ((neededBits % BITS_PER_BYTE) ? 1 : 0); + int oldSize = (parcBitVector->bitLength / BITS_PER_BYTE) + ((parcBitVector->bitLength % BITS_PER_BYTE) ? 1 : 0); + + uint8_t *newArray = parcMemory_Reallocate(parcBitVector->bitArray, newSize); + assertTrue(newArray, "parcMemory_Reallocate(%d) failed", newSize); + // Reallocate does not guarantee that additional memory is zero-filled. + memset((void *) &(newArray[oldSize]), parcBitVector->fillValue, newSize - oldSize); + + parcBitVector->bitArray = newArray; + assertNotNull(newArray, "parcMemory_Reallocate(%d) returned NULL", newSize); + parcBitVector->bitLength = newSize * BITS_PER_BYTE; + } +} + +int +parcBitVector_Get(const PARCBitVector *parcBitVector, unsigned bit) +{ + assertTrue(bit < MAX_BIT_VECTOR_INDEX, "parcBitVector_Set passed a bit index that's too large"); + + if ((parcBitVector == NULL) || (bit >= parcBitVector->bitLength)) { + return -1; + } + + int byte = bit / BITS_PER_BYTE; + int bitInByte = bit % BITS_PER_BYTE; + if ((parcBitVector->bitArray[byte] & (1 << bitInByte))) { + return 1; + } + + return 0; +} + +void +parcBitVector_Set(PARCBitVector *parcBitVector, unsigned bit) +{ + assertNotNull(parcBitVector, "parcBitVector_Set passed a NULL parcBitVector"); + assertTrue(bit < MAX_BIT_VECTOR_INDEX, "parcBitVector_Set passed a bit index that's too huge"); + if (bit >= parcBitVector->bitLength) { + _parc_bit_vector_resize(parcBitVector, bit); + } + int byte = bit / BITS_PER_BYTE; + int bitInByte = bit % BITS_PER_BYTE; + if (!(parcBitVector->bitArray[byte] & (1 << bitInByte))) { + parcBitVector->bitArray[byte] |= (1 << bitInByte); + parcBitVector->numberOfBitsSet++; + } + if ((parcBitVector->firstBitSet == -1) || (bit < parcBitVector->firstBitSet)) { + parcBitVector->firstBitSet = bit; + } +} + +void +parcBitVector_SetVector(PARCBitVector *parcBitVector, const PARCBitVector *bitsToSet) +{ + assertNotNull(parcBitVector, "parcBitVector_SetVector passed a NULL parcBitVector"); + assertNotNull(parcBitVector, "parcBitVector_SetVector passed a NULL vector of bits to set"); + int settingBit = 0; + for (int i = 0; i < bitsToSet->numberOfBitsSet; i++) { + settingBit = parcBitVector_NextBitSet(bitsToSet, settingBit); + assertTrue(settingBit != -1, "Number of bits claimed set inconsistent with bits found"); + parcBitVector_Set(parcBitVector, settingBit); + settingBit++; + } +} + +void +parcBitVector_Reset(PARCBitVector *parcBitVector) +{ + parcBitVector->numberOfBitsSet = 0; + parcBitVector->firstBitSet = -1; + parcBitVector->fillValue = 0; + memset(parcBitVector->bitArray, parcBitVector->fillValue, parcBitVector->bitLength / BITS_PER_BYTE); +} + + +void +parcBitVector_Clear(PARCBitVector *parcBitVector, unsigned bit) +{ + assertNotNull(parcBitVector, "parcBitVector_Clear passed a NULL parcBitVector"); + assertTrue(bit < MAX_BIT_VECTOR_INDEX, "parcBitVector_Clear passed a bit index that's too huge"); + if (bit > parcBitVector->bitLength) { + _parc_bit_vector_resize(parcBitVector, bit); + } + int byte = bit / BITS_PER_BYTE; + int bitInByte = bit % BITS_PER_BYTE; + if (parcBitVector->bitArray[byte] & (1 << bitInByte)) { + parcBitVector->bitArray[byte] &= ~(1 << bitInByte); + parcBitVector->numberOfBitsSet--; + } + if (bit == parcBitVector->firstBitSet) { + parcBitVector->firstBitSet = parcBitVector_NextBitSet(parcBitVector, bit + 1); + } +} + +void +parcBitVector_ClearVector(PARCBitVector *parcBitVector, const PARCBitVector *bitsToClear) +{ + assertNotNull(parcBitVector, "parcBitVector_ClearVector passed a NULL parcBitVector"); + assertNotNull(bitsToClear, "parcBitVector_ClearVector passed a NULL for vector of bitsToClear"); + + // If we're clearing ourself, we just need a reset + if (parcBitVector == bitsToClear) { + parcBitVector_Reset(parcBitVector); + return; + } + + int clearingBit = 0; + for (int i = 0; i < bitsToClear->numberOfBitsSet; i++) { + clearingBit = parcBitVector_NextBitSet(bitsToClear, clearingBit); + // only clear up to the end of the original vector + if (clearingBit >= parcBitVector->bitLength) { + return; + } + if (clearingBit != -1) { + parcBitVector_Clear(parcBitVector, clearingBit); + } + clearingBit++; + } +} + +unsigned +parcBitVector_NumberOfBitsSet(const PARCBitVector *parcBitVector) +{ + return parcBitVector->numberOfBitsSet; +} + +unsigned +parcBitVector_NextBitSet(const PARCBitVector *parcBitVector, unsigned startFrom) +{ + if (startFrom >= parcBitVector->bitLength) { + return -1; + } + if (startFrom <= parcBitVector->firstBitSet) { + return parcBitVector->firstBitSet; + } + int byte = startFrom / BITS_PER_BYTE; + int bitInByte = startFrom % BITS_PER_BYTE; + int allocatedBytes = ((parcBitVector->bitLength) / BITS_PER_BYTE); + for (; byte < allocatedBytes; byte++) { + if (parcBitVector->bitArray[byte]) { + for (; bitInByte < BITS_PER_BYTE; bitInByte++) { + if (parcBitVector->bitArray[byte] & (1 << bitInByte)) { + return (byte * BITS_PER_BYTE) + bitInByte; + } + } + } + bitInByte = 0; + } + return -1; +} + +bool +parcBitVector_Contains(const PARCBitVector *parcBitVector, const PARCBitVector *testVector) +{ + bool result = true; + + int testBit = 0; + for (int i = 0; i < testVector->numberOfBitsSet; i++, testBit++) { + testBit = parcBitVector_NextBitSet(testVector, testBit); + if (parcBitVector_Get(parcBitVector, testBit) != 1) { + result = false; + break; + } + } + + return result; +} + +char * +parcBitVector_ToString(const PARCBitVector *parcBitVector) +{ + char *result = NULL; + + PARCBufferComposer *composer = parcBufferComposer_Create(); + if (composer != NULL) { + int nextBitSet = 0; + parcBufferComposer_Format(composer, "[ "); + for (int index = parcBitVector_NumberOfBitsSet(parcBitVector); index; index--) { + nextBitSet = parcBitVector_NextBitSet(parcBitVector, nextBitSet); + parcBufferComposer_Format(composer, "%d ", nextBitSet); + nextBitSet++; + } + parcBufferComposer_Format(composer, "]"); + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + parcBufferComposer_Release(&composer); + + result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + } + + return result; +} + +PARCBitVector * +parcBitVector_Or(const PARCBitVector *first, const PARCBitVector *second) +{ + PARCBitVector *result = NULL; + + if (first != NULL) { + result = parcBitVector_Copy(first); + if (second != NULL) { + for (int bit = 0; (bit = parcBitVector_NextBitSet(second, bit)) >= 0; bit++) { + parcBitVector_Set(result, bit); + } + } + } else if (second != NULL) { + result = parcBitVector_Copy(second); + } else { // both null, or is empty + result = parcBitVector_Create(); + } + + return result; +} + +PARCBitVector * +parcBitVector_And(const PARCBitVector *first, const PARCBitVector *second) +{ + PARCBitVector *result = parcBitVector_Create(); + + if (second == NULL) { + if (first != NULL) { + return result; + } + } + + if (first != NULL) { + for (int bit = 0; (bit = parcBitVector_NextBitSet(first, bit)) >= 0; bit++) { + if (parcBitVector_Get(second, bit) == 1) { + parcBitVector_Set(result, bit); + } + } + } + + return result; +} + +static PARCBitVector * +_parcBitVector_LeftShiftOnce(PARCBitVector *parcBitVector) +{ + if (parcBitVector != NULL) { + for (int bit = 0; (bit = parcBitVector_NextBitSet(parcBitVector, bit)) >= 0; bit++) { + if (bit > 0) { // The first bit falls off + parcBitVector_Set(parcBitVector, bit - 1); + } + parcBitVector_Clear(parcBitVector, bit); + } + } + + return parcBitVector; +} + +PARCBitVector * +parcBitVector_LeftShift(PARCBitVector *parcBitVector, size_t count) +{ + for (int i = 0; i < count; i++) { + _parcBitVector_LeftShiftOnce(parcBitVector); + } + + return parcBitVector; +} + +static PARCBitVector * +_parcBitVector_RightShiftOnce(PARCBitVector *parcBitVector) +{ + if (parcBitVector != NULL) { + for (int bit = 0; (bit = parcBitVector_NextBitSet(parcBitVector, bit)) >= 0; bit++) { + // Shift the next sequence of one bits into the first zero bit + int nextZero = bit + 1; + while (parcBitVector_Get(parcBitVector, nextZero) == 1) { + nextZero++; + } + parcBitVector_Clear(parcBitVector, bit++); + while (bit <= nextZero) { + parcBitVector_Set(parcBitVector, bit); + bit++; + } + } + } + + return parcBitVector; +} + +PARCBitVector * +parcBitVector_RightShift(PARCBitVector *parcBitVector, size_t count) +{ + for (int i = 0; i < count; i++) { + _parcBitVector_RightShiftOnce(parcBitVector); + } + + return parcBitVector; +} diff --git a/libparc/parc/algol/parc_BitVector.h b/libparc/parc/algol/parc_BitVector.h new file mode 100755 index 00000000..7e72ac70 --- /dev/null +++ b/libparc/parc/algol/parc_BitVector.h @@ -0,0 +1,426 @@ +/* + * 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. + */ + +/** + * @file parc_BitVector.h + * @ingroup types + * + */ +#ifndef libparc_parc_BitVector_h +#define libparc_parc_BitVector_h + +#include <stdbool.h> + +/** + * @typedef PARCBitVector + * @brief A structure containing private bit vector state data variables + */ +struct PARCBitVector; +typedef struct PARCBitVector PARCBitVector; + +/** + * Create a new bit vector instance. + * + * @returns NULL on error, pointer to new vector on success. + * + * Example: + * @code + * { + * PARCBitVector *parcBitVector = parcBitVector_Create(); + * } + * @endcode + * + */ +PARCBitVector *parcBitVector_Create(void); + +/** + * Create a copy of a bit vector instance. + * + * @param [in] parcBitVector to duplicate + * @returns NULL on error, pointer to new copy on success. + * + * Example: + * @code + * { + * PARCBitVector *parcBitVector = parcBitVector_Create(); + * parcBitVector_Set(parcBitVector, 10); + * PARCBitVector *copy = parcBitVector_Copy(parcBitVector); + * assertTrue(parcBitVector_Equals(parcBitVector, copy), "Duplicate vector is unequal"); + * } + * @endcode + * + */ +PARCBitVector *parcBitVector_Copy(const PARCBitVector *parcBitVector); + +/** + * Obtain a reference to a bit vector instance. + * + * @param [in] parcBitVector to obtain reference to + * @returns pointer to BitVector + * + * Example: + * @code + * { + * PARCBitVector *parcBitVector = parcBitVector_Create(); + * PARCBitVector *reference = parcBitVector_Acquire(parcBitVector); + * parcBitVector_Release(&reference); + * parcBitVector_Release(&parcBitVector); + * } + * @endcode + * + */ +PARCBitVector *parcBitVector_Acquire(const PARCBitVector *parcBitVector); + +/** + * Release a reference to a bit vector instance. + * + * @param [in] parcBitVector to release reference to + * + * Example: + * @code + * { + * PARCBitVector *parcBitVector = parcBitVector_Create(); + * PARCBitVector *reference = parcBitVector_Acquire(parcBitVector); + * parcBitVector_Release(&reference); + * parcBitVector_Release(&parcBitVector); + * } + * @endcode + * + */ +void parcBitVector_Release(PARCBitVector **parcBitVector); + +/** + * Determine equality of a pair of bit vectors + * + * @param [in] a bit vector to compare + * @param [in] b bit vector to compare + * @returns true if equal, false if unequal + * + * Example: + * @code + * { + * PARCBitVector *parcBitVector = parcBitVector_Create(); + * parcBitVector_Set(parcBitVector, 10); + * PARCBitVector *copy = parcBitVector_Copy(parcBitVector); + * parcBitVector_Set(copy, 1); + * assertTrue(parcBitVector_Equals(parcBitVector, copy) == false, "Vector should have been unequal"); + * } + * @endcode + * + */ +bool parcBitVector_Equals(const PARCBitVector *a, const PARCBitVector *b); + + +/** + * Determine equality of a pair of bit vectors + * + * @param [in] parcBitVector bit vector to search + * @param [in] testVector bit vector to test + * @returns true if parcBitVector contains all bits in testVector, false otherwise + * + * Example: + * @code + * { + * PARCBitVector *superSet = parcBitVector_Create(); + * parcBitVector_Set(superSet, 10); + * parcBitVector_Set(superSet, 11); + * PARCBitVector *subSet = parcBitVector_Create(); + * parcBitVector_Set(subSet, 10); + * assertTrue(parcBitVector_Contains(superSet, subSet), "Expect superSet to contain subSet"); + * } + * @endcode + * + */ +bool parcBitVector_Contains(const PARCBitVector *parcBitVector, const PARCBitVector *testVector); + +/** + * Get the current value of a bit in a vector + * + * @param [in] parcBitVector to obtain value from + * @param [in] bit in vector to get value of + * @returns value of bit in vector, 1 or 0 + * + * Example: + * @code + * { + * PARCBitVector *parcBitVector = parcBitVector_Create(); + * parcBitVector_Set(parcBitVector, 10); + * assertTrue(parcBitVector_Get(parcBitVector, 10) == 1, "Vector should have been set"); + * } + * @endcode + * + */ +int parcBitVector_Get(const PARCBitVector *parcBitVector, unsigned bit); + +/** + * Set a bit in a vector + * + * @param [in] parcBitVector to set bit in + * @param [in] bit in vector to set + * + * Example: + * @code + * { + * PARCBitVector *parcBitVector = parcBitVector_Create(); + * parcBitVector_Set(parcBitVector, 10); + * assertTrue(parcBitVector_Get(parcBitVector, 10) == 1, "Vector should have been set"); + * } + * @endcode + * + */ +void parcBitVector_Set(PARCBitVector *parcBitVector, unsigned bit); + +/** + * Right shift a vector contents + * + * @param [in] parcBitVector + * @param [in] rightShift count + * @param [out] input vector + * + * Example: + * @code + * { + * PARCBitVector *parcBitVector = parcBitVector_Create(); + * parcBitVector_Set(parcBitVector, 0); + * + * parcBitVector_RightShift(parcBitVector, 1); + * assertTrue(parcBitVector_FirstBitSet(parcBitVector) == 1, + * "First vector element should have moved up"); + * + * parcBitVector_Release(&parcBitVector); + * } + * @endcode + * + */ +PARCBitVector *parcBitVector_RightShift(PARCBitVector *parcBitVector, size_t rightShift); + +/** + * Left shift a vector contents + * + * @param [in] parcBitVector + * @param [in] leftShift count + * @param [out] input vector + * + * Example: + * @code + * { + * PARCBitVector *parcBitVector = parcBitVector_Create(); + * parcBitVector_Set(parcBitVector, 10); + * parcBitVector_Set(parcBitVector, 0); + * + * parcBitVector_LeftShift(parcBitVector, 1); + * assertTrue(parcBitVector_NumberOfBitsSet(parcBitVector) == 1, + * "First vector element should have rolled off"); + * + * parcBitVector_Release(&parcBitVector); + * } + * @endcode + * + */ +PARCBitVector *parcBitVector_LeftShift(PARCBitVector *parcBitVector, size_t leftShift); + +/** + * Logical And of a vector contents with another vector + * + * @param [in] a vector + * @param [in] b vector + * @param [out] allocated vector with result of And operation + * + * Example: + * @code + * { + * PARCBitVector *parcBitVector = parcBitVector_Create(); + * parcBitVector_Set(parcBitVector, 10); + * PARCBitVector *andVector = parcBitVector_Create(); + * parcBitVector_Set(andVector, 11); + * + * PARCBitVector *result = parcBitVector_And(parcBitVector, andVector); + * assertTrue(parcBitVector_NumberOfBitsSet(result) == 0, "Vector should have been empty"); + * + * parcBitVector_Release(&parcBitVector); + * parcBitVector_Release(&andVector); + * parcBitVector_Release(&result); + * } + * @endcode + * + */ +PARCBitVector *parcBitVector_And(const PARCBitVector *a, const PARCBitVector *b); + +/** + * Logical Or of a vector contents with another vector + * + * @param [in] a vector + * @param [in] b vector + * @param [out] allocated vector with result of Or operation + * + * Example: + * @code + * { + * PARCBitVector *parcBitVector = parcBitVector_Create(); + * parcBitVector_Set(parcBitVector, 10); + * PARCBitVector *orVector = parcBitVector_Create(); + * parcBitVector_Set(orVector, 11); + * + * PARCBitVector *result = parcBitVector_Or(parcBitVector, orVector); + * assertTrue(parcBitVector_NumberOfBitsSet(result) == 2, "Vector should have been set"); + * + * parcBitVector_Release(&parcBitVector); + * parcBitVector_Release(&orVector); + * parcBitVector_Release(&result); + * } + * @endcode + * + */ +PARCBitVector *parcBitVector_Or(const PARCBitVector *a, const PARCBitVector *b); + +/** + * Set a vector of bits in a vector + * + * @param [in] parcBitVector to set bits in + * @param [in] bitsToSet vector of bits to set + * + * Example: + * @code + * { + * PARCBitVector *parcBitVector = parcBitVector_Create(); + * PARCBitVector *bitsToSet = parcBitVector_Create(); + * parcBitVector_Set(bitsToSet, 10); + * parcBitVector_SetVector(parcBitVector, bitsToSet); + * assertTrue(parcBitVector_Get(parcBitVector, 10) == 1, "Vector should have been set"); + * } + * @endcode + * + */ +void parcBitVector_SetVector(PARCBitVector *parcBitVector, const PARCBitVector *bitsToSet); + +/** + * Reset the bits of bit vector to 0 + * + * @param [in] parcBitVector to set bits in + * + * Example: + * @code + * { + * PARCBitVector *parcBitVector = parcBitVector_Create(); + * parcBitVector_Set(parcBitVector, 10); + * parcBitVector_Set(parcBitVector, 42); + * parcBitVector_Reset(parcBitVector); + * assertTrue(parcBitVector_NumberOfBitsSet(parcBitVector) == 0, "Vector should have 0 bits set"); + * } + * @endcode + * + */ +void parcBitVector_Reset(PARCBitVector *parcBitVector); + +/** + * Clear a bit in a vector + * + * @param [in] parcBitVector to clear bit in + * @param [in] bit in vector to clear + * + * Example: + * @code + * { + * PARCBitVector *parcBitVector = parcBitVector_Create(); + * parcBitVector_Set(parcBitVector, 10); + * assertTrue(parcBitVector_Get(parcBitVector, 10) == 1, "Vector should have been set"); + * parcBitVector_Clear(parcBitVector, 10); + * assertTrue(parcBitVector_Get(parcBitVector, 10) == 0, "Vector should have been cleared"); + * } + * @endcode + * + */ +void parcBitVector_Clear(PARCBitVector *parcBitVector, unsigned bit); + +/** + * Clear a vector of bits in a vector + * + * @param [in] parcBitVector to clear bits in + * @param [in] bitsToClear vector of bits to clear + * + * Example: + * @code + * { + * PARCBitVector *parcBitVector = parcBitVector_Create(); + * parcBitVector_Set(parcBitVector, 10); + * PARCBitVector *bitsToClear = parcBitVector_Create(); + * parcBitVector_Set(bitsToClear, 10); + * parcBitVector_SetVector(parcBitVector, bitsToClear); + * assertTrue(parcBitVector_Get(parcBitVector, 10) == 0, "Vector should have been cleared"); + * } + * @endcode + * + */ +void parcBitVector_ClearVector(PARCBitVector *parcBitVector, const PARCBitVector *bitsToClear); + +/** + * Return number of bits set in a vector + * + * @param [in] parcBitVector to return number of bits set from + * @returns number of bits currently set in bit vector + * + * Example: + * @code + * { + * PARCBitVector *parcBitVector = parcBitVector_Create(); + * parcBitVector_Set(parcBitVector, 10); + * assertTrue(parcBitVector_NumberOfBitsSet(parcBitVector) == 1, "One bit should have been set"); + * } + * @endcode + * + */ +unsigned parcBitVector_NumberOfBitsSet(const PARCBitVector *parcBitVector); + +/** + * Return index if next set bit in vector + * + * @param [in] parcBitVector to inspect + * @param [in] startFrom bit position to start inspection from + * @returns index of next bit set in vector after startFrom or -1 if none + * + * Example: + * @code + * { + * PARCBitVector *parcBitVector = parcBitVector_Create(); + * parcBitVector_Set(parcBitVector, 10); + * parcBitVector_Set(parcBitVector, 12); + * assertTrue(parcBitVector_NextBitSet(parcBitVector, 0) == 10, "Bit 10 should have been found first"); + * assertTrue(parcBitVector_NextBitSet(parcBitVector, 11) == 12, "Bit 12 should have been found next"); + * } + * @endcode + * + */ +unsigned parcBitVector_NextBitSet(const PARCBitVector *parcBitVector, unsigned startFrom); + +/** + * Return text representation of a bit vector + * + * @param [in] parcBitVector to represent + * @returns allocated character string representing bit vector which must be released by parcMemory_Deallocate + * + * Example: + * @code + * { + * PARCBitVector *parcBitVector = parcBitVector_Create(); + * parcBitVector_Set(parcBitVector, 1); + * const char *bitVectorString = parcBitVector_ToString(parcBitVector); + * printf("Vector contents: %s\n", bitVectorString); + * parcMemory_Deallocate(&bitVectorString); + * } + * @endcode + * + */ +char *parcBitVector_ToString(const PARCBitVector *parcBitVector); +#endif // libparc_parc_BitVector_h 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; +} diff --git a/libparc/parc/algol/parc_Buffer.h b/libparc/parc/algol/parc_Buffer.h new file mode 100644 index 00000000..2c7f56fd --- /dev/null +++ b/libparc/parc/algol/parc_Buffer.h @@ -0,0 +1,1665 @@ +/* + * 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. + */ + +/** + * @file parc_Buffer.h + * @ingroup memory + * @brief An indexable, linear buffer of bytes. + * + * A `PARCBuffer` is a linear, finite sequence of bytes. + * The essential properties of a buffer are its content, its capacity, limit, and position: + * + * @htmlonly + * <svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" version="1.1" viewBox="48 143 304 126" width="304pt" height="126pt"> + * <defs> + * <font-face font-family="Helvetica Neue" font-size="10" panose-1="2 0 5 3 0 0 0 2 0 4" units-per-em="1000" underline-position="-100" underline-thickness="50" slope="0" x-height="517" cap-height="714" ascent="951.99585" descent="-212.99744" font-weight="500"> + * <font-face-src> + * <font-face-name name="HelveticaNeue"/> + * </font-face-src> + * </font-face> + * <marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="black"> + * <g> + * <path d="M 8 0 L 0 -3 L 0 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/> + * </g> + * </marker> + * </defs> + * <g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"> + * <title>icon_512x512</title> + * <rect fill="white" width="512" height="512"/> + * <g> + * <title>Layer 1</title> + * <text transform="translate(73 231.5)" fill="black"> + * <tspan font-family="Helvetica Neue" font-size="10" font-weight="500" x=".055" y="10" textLength="13.37">Off</tspan> + * <tspan font-family="Helvetica Neue" font-size="10" font-weight="500" x="13.425" y="10" textLength="13.52">set</tspan> + * </text> + * <text transform="translate(178.5 231)" fill="black"> + * <tspan font-family="Helvetica Neue" font-size="10" font-weight="500" x=".445" y="10" textLength="36.11">Position</tspan> + * <tspan font-family="Helvetica Neue" font-size="10" font-weight="500" x="7.2" y="22" textLength="22.6">Mark</tspan> + * </text> + * <text transform="translate(294 231.5)" fill="black"> + * <tspan font-family="Helvetica Neue" font-size="10" font-weight="500" x=".16" y="10" textLength="21.68">Limit</tspan> + * </text> + * <text transform="translate(271 157.5)" fill="black"> + * <tspan font-family="Helvetica Neue" font-size="10" font-weight="500" x=".185" y="10" textLength="39.63">Capacity</tspan> + * </text> + * <text transform="translate(83 157.5)" fill="black"> + * <tspan font-family="Helvetica Neue" font-size="10" font-weight="500" x=".245" y="10" textLength="23.51">Array</tspan> + * </text> + * <path d="M 78 165.48333 C 73.6671 165.98884 67.757544 163.49623 65 167 C 63.49089 168.91749 62.925312 172.63153 62.52875 176.66717" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + * <rect x="59" y="187" width="281" height="13" fill="#205469"/> + * <rect x="59" y="187" width="281" height="13" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + * <path d="M 316 166.11628 C 321.9994 166.74412 330.42197 164.60163 334 168 C 335.8631 169.76954 336.41326 173.04195 336.67595 176.64326" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + * <rect x="116" y="181" width="160" height="27" fill="#f60"/> + * <rect x="116" y="181" width="160" height="27" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + * <path d="M 105 234.05085 C 108.6663 233.3673 114.16685 236.34137 116 232 C 117.15073 229.27477 116.856755 223.66586 116.47841 217.88884" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + * <path d="M 289 235.56897 C 284.6671 235.04603 278.16645 238.59437 276 234 C 274.57827 230.98495 275.02256 224.46193 275.496 217.88448" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + * <line x1="173.5" y1="232.84568" x2="125.08789" y2="211.92687" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + * <line x1="220.5" y1="232.58861" x2="266.94855" y2="212.01014" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + * </g> + * </g> + * </svg> + * @endhtmlonly + * + * * A buffer's capacity is the number of bytes it contains. + * The capacity of a buffer is never negative and never changes. + * + * * A buffer's limit is the index of the first byte that should not be read or written. + * A buffer's limit is never negative and is never greater than its capacity. + * + * * A buffer's position is a cursor to or index of the next byte to be read or written. + * A buffer's position is never negative and is never greater than its limit. + * + * A PARCBuffer's capacity may be larger than the extent of data manipulated by the buffer. + * The extent of the data manipulated by the buffer is manipulated via: + * * {@link parcBuffer_Position}, + * * {@link parcBuffer_SetPosition}, + * * {@link parcBuffer_Limit}, + * * {@link parcBuffer_SetLimit} and + * * {@link parcBuffer_Flip}. + * + * Strictly speaking, these relations are always true: _0 <= mark <= position <= limit <= capacity_ + * + * The general model for use is to: + * * Create a buffer using a form of {@link parcBuffer_Allocate} or {@link parcBuffer_Wrap}. + * * Optionally insert data into the buffer via put operations, + * ultimately setting the position at the end of the valid data. + * * 'Flip' the buffer using the {@link parcBuffer_Flip} function to set the position to 0 and the limit at the end + * of the valid data. + * * Optionally get data from the buffer via one of the many get operations. + * * Use {@link parcBuffer_Rewind} function to set the position to 0 again, leaving the limit at the end of the valid data. + * + * Data is placed into a `PARCBuffer` via `Put` functions, and retreived from the buffer via `Get` operations. + * Both `Put` and `Get` perform their operations at the position of the buffer and update the position to the location of the + * next element of data. + * Both `Put` and `Get` operations have a full compliment of intrinsic data types that operate on data at + * relative positions in the buffer. + * + * The function {@link parcBuffer_GetAtIndex} provides absolute index access to the buffer for bytes. + * + * * {@link parcBuffer_PutUint8}, + * * {@link parcBuffer_PutUint16}, + * * {@link parcBuffer_PutUint32}, + * * {@link parcBuffer_PutUint64}, + * * {@link parcBuffer_PutAtIndex} + * + * * {@link parcBuffer_GetUint8}, + * * {@link parcBuffer_GetUint16}, + * * {@link parcBuffer_GetUint32}, + * * {@link parcBuffer_GetUint64}, + * * {@link parcBuffer_GetAtIndex} + * + */ +#ifndef libparc_parc_Buffer_h +#define libparc_parc_Buffer_h + +typedef struct parc_buffer PARCBuffer; + +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_ByteArray.h> + +extern parcObjectDescriptor_Declaration(PARCBuffer); + +#ifdef PARCLibrary_DISABLE_VALIDATION +# define parcBuffer_OptionalAssertValid(_instance_) +#else +# define parcBuffer_OptionalAssertValid(_instance_) parcBuffer_AssertValid(_instance_) +#endif + +/** + * Assert that an instance of `PARCBuffer` is valid. + * + * If the instance is not valid, terminate via `trapIllegalValue()` + * + * Valid means the internal state of the type is consistent with its required current or future behaviour. + * This may include the validation of internal instances of types. + * + * @param [in] instance A pointer to a `PARCBuffer` instance. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(64); + * + * parcBuffer_AssertValid(array); + * } + * @endcode + * @see parcBuffer_OptionalAssertValid + */ +void parcBuffer_AssertValid(const PARCBuffer *instance); + +/** + * Determine if an instance of `PARCBuffer` is valid. + * + * + * Valid means the internal state of the type is consistent with its required current or future behaviour. + * This may include the validation of internal instances of types. + * + * @param [in] buffer A pointer to a `PARCBuffer` instance. + * + * @return true The instance is valid. + * @return false The instance is not valid. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(64); + * + * if (parcBuffer_IsValid(buffer)) { + * printf("Buffer is valid.\n"); + * } + * } + * @endcode + */ +bool parcBuffer_IsValid(const PARCBuffer *buffer); + +/** + * Create a new instance of `PARCBuffer` using dynamically allocated memory. + * + * The new buffer's position will be zero, + * its limit will be set to `length`, + * its mark will be undefined, + * and each of its elements will be initialized to zero. + * + * If capacity is zero, the buffer contains no underlying byte array. + * + * @param [in] capacity The number of bytes to allocate. + * + * @return NULL Memory could not be allocated. + * @return non-NULL A pointer to a `PARCBuffer` instance. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(64); + * + * parcBuffer_Release(&&buffer); + * } + * @endcode + */ +PARCBuffer *parcBuffer_Allocate(size_t capacity); + +/** + * Create a new instance of `PARCBuffer` using using program supplied static memory (rather than allocated). + * + * The new buffer will be backed by the given array, @p array. + * Modifications to the buffer will cause the array to be modified and vice versa. + * + * The new buffer's capacity will be @p arrayLength, + * its initial position will be @p position , + * the index of the first byte that should not be read or written will be @p limit, + * and its mark will be undefined. + * + * In all cases, _0 <= position <= limit <= capacity_ + * + * Its backing array will be the given array, starting at index 0 of that array. + * + * @param [in] array A pointer to a memory array. + * @param [in] arrayLength The length, in `uint8_t` units, of the memory array. + * @param [in] position The initial value for the buffer's position. + * @param [in] limit The initial value for the buffer's limit. + * + * @return A `PARCBuffer` pointer. + * + * Example: + * @code + * { + * uint8_t array[64]; + * + * PARCBuffer *buffer = parcBuffer_Wrap(array, sizeof(array), 0, sizeof(array)); + * + * parcBuffer_Release(&buffer); + * } + * @endcode + * + * @see parcBuffer_Allocate + * @see parcBuffer_Release + */ +PARCBuffer *parcBuffer_Wrap(void *array, size_t arrayLength, size_t position, size_t limit); + +/** + * Create a new instance of `PARCBuffer` using referencing the given {@link PARCByteArray}. + * + * A reference to the `PARCByteArray` is acquired. + * + * The new buffer will be backed by the given `PARCByteArray`. + * Modifications to the buffer will cause the array to be modified and vice versa. + * + * The new buffer's capacity will be @p arrayLength, + * its initial position will be @p position , + * the index of the first byte that should not be read or written will be @p limit, + * and its mark will be undefined. + * + * In all cases, _0 <= position <= limit <= capacity_ + * + * The new buffer's + * capacity will be the length of the `PARCByteArray`, + * its initial position will be @p position , + * the index of the first byte that should not be read or written will be @p limit, + * and its mark will be undefined. + * + * @param [in] byteArray A pointer to a `PARCByteArray` instance. + * @param [in] position The initial value for the buffer's position. + * @param [in] limit The initial value for the buffer's limit which must be less than or equal to the PARCByteArray's capacity. + * + * @return A `PARCBuffer` pointer. + * + * Example: + * @code + * { + * PARCByteArray *array = parcByteArray_Allocate(64); + * + * PARCBuffer *buffer = parcBuffer_WrapByteArray(array, 0, parcByteArray_Capacity(array)); + * + * parcBuffer_Release(&&buffer); + * } + * @endcode + * + * @see parcBuffer_Allocate + * @see parcBuffer_Wrap + */ +PARCBuffer *parcBuffer_WrapByteArray(PARCByteArray *byteArray, size_t position, size_t limit); + +/** + * Create a new instance of `PARCBuffer` wrapping the given null-terminated C string as its value. + * + * The new buffer's capacity will be the length of the string excluding the terminating nul character. + * its initial position will be 0, + * the index of the first byte that should not be read or written will be @p limit, + * and its mark will be undefined. + * + * @param [in] string A pointer to a C-string to copy and then wrap. + * + * @return A `PARCBuffer` pointer. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_WrapCString("Hello World"); + * + * parcBuffer_Release(&buffer); + * } + * @endcode + * + * @see parcBuffer_Allocate + * @see parcBuffer_Wrap + */ +PARCBuffer *parcBuffer_WrapCString(char *string); + +/** + * Create a new instance of a `PARCBuffer` copying the given null-terminated C string as its value. + * + * The new buffer's capacity will be the length of the string excluding the terminating nul character. + * its initial position will be 0, + * the index of the first byte that should not be read or written will be @p limit, + * and its mark will be undefined. + * + * @param [in] string A pointer to C-string to copy and then wrap. + * + * @return A `PARCBuffer` pointer. + * + * Example: + * @code + * { + * PARCByteArray *buffer = parcBuffer_AllocateCString("test string"); + * + * parcBUffer_Release(&buffer); + * } + * @endcode + */ +PARCBuffer *parcBuffer_AllocateCString(const char *string); + +/** + * Create a `PARCBuffer` initalised with a copy of the contents of given byte array. + * + * The length must be non-negative (> 0) and the array pointer must not be NULL. + * The contents of the given array are used to initialize the `PARCBuffer` instance, + * and the size of the new instance is equal to the specified length (just wide enough to fit the array). + * + * @param [in] bytes A pointer to an array of bytes. + * @param [in] length The number of bytes to copy to the `PARCBuffer`. + * + * @return A newly allocated `PARCBuffer` instance that must be freed via `parcBuffer_Release()`. + * + * Example: + * @code + * { + * unsigned char array[] = { 1, 2, 3, 4, 5 }; + * PARCBuffer *buffer = parcBuffer_CreateFromArray(array, sizeof(array)); + * + * parcBuffer_Release(&buffer); + * } + * @endcode + */ +PARCBuffer *parcBuffer_CreateFromArray(const void *bytes, size_t length); + +/** + * Parse a null-terminated hexadecimal string to create a new `PARCBuffer` instance. + * + * The hex string must be null-terminated so parsing is deterministic and correct. + * The hex string parameter is not modified in any way. + * The hex string must be an even length greater than zero. + * + * @param [in] hexString The hex string to parse. + * + * @return NULL The string could not be parsed + * @return A new `PARCElasticBuffer` instance. + * + * Example: + * @code + * { + * char *expected = "0123456789ABCDEF"; + * PARCBuffer *buffer = parcBuffer_Flip(parcBuffer_ParseHexString(expected)); + * printf("String: %s\n", parcBuffer_ToString(buffer)); + * + * parcBuffer_Release(buffer); + * } + * @endcode + */ +PARCBuffer *parcBuffer_ParseHexString(const char *hexString); + +/** + * Increase or decrease the capacity of an existing PARCBuffer. + * + * If the new capacity is greater than the old capacity and the limit is currently set to the old capacity, + * then set the new limit to the new capacity. + * Otherwise, if the limit is not currently set to the capacity, then leave the limit unchanged. + * + * If the new capacity is less than the old capacity and the limit is currently set to the old capacity, + * then set the new limit to the new capacity. + * Otherwise, set the limit to the the lesser of the old limit or the new capacity. + * If the limit is not currently set to the capacity, + * the set the limit to the the lesser of the old limit or the new capacity. + * + * If the original mark exceeds the new limit, the new mark is invalidated and any subsequent + * operation on the resulting `PARCBuffer` that requires the mark will abort until the mark + * is set again via `parcBuffer_Mark`. + * + * If the original position of the buffer is beyond the new limit of the buffer, the position is set to the new limit. + * + * The contents of the old buffer are preserved from the origin to the new limit. + * + * This operation may induce a memory copy. + * As a consequence, any `PARCBuffer` instances previously created via {@link parcBuffer_Slice} + * refer to memory previously used by this `PARCBuffer`. + * + * A PARCBuffer originally created via any of the `parcBuffer_Wrap` forms, + * may no longer refer to the original wrapped data. + * + * @param [in] buffer A pointer to a valid `PARCBuffer` instance. + * @param [in] capacity The new capacity of `PARCBuffer` + * + * @return PARCBuffer A new `PARCBuffer` instance initialized with the contents of the given buffer. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_WrapCString("Hello World"); + * parcBuffer_Resize(buffer, 4); + * + * parcBuffer_Release(&buffer); + * } + * @endcode + * + * @see parcBuffer_Allocate + * @see parcBuffer_Wrap + */ +PARCBuffer *parcBuffer_Resize(PARCBuffer *buffer, size_t capacity); + +/** + * Increase the number of references to a `PARCBuffer`. + * + * Note that new `PARCBuffer` is not created, + * only that the given `PARCBuffer` reference count is incremented. + * Discard the reference by invoking `parcBuffer_Release`. + * + * @param [in] buffer A pointer to a `PARCBuffer` instance. + * + * @return The input `PARCBuffer` pointer. + * + * Example: + * @code + * { + * PARCBuffer *x = parcBuffer_Allocate(10); + * + * PARCBuffer *x_2 = parcBuffer_Acquire(x); + * + * parcBuffer_Release(&x); + * parcBuffer_Release(&x_2); + * } + * @endcode + */ +PARCBuffer *parcBuffer_Acquire(const PARCBuffer *buffer); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] bufferPtr A pointer to a pointer to the instance to release. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * + * parcBuffer_Release(&buffer); + * } + * @endcode + */ +void parcBuffer_Release(PARCBuffer **bufferPtr); + +/** + * Returns this buffer's capacity. + * + * @param [in] buffer A pointer to a `PARCBuffer` instance. + * + * @return The given buffer's capacity. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * + * size_t capacity = parcBuffer_Capacity(buffer); + * + * parcBuffer_Release(&capacity); + * } + * @endcode + */ +size_t parcBuffer_Capacity(const PARCBuffer *buffer); + +/** + * Clear the given buffer restoring it to its initial state: + * The position is set to zero, + * the limit is set to the capacity, + * and the mark is invalidated. + * + * The mark is made invalid and any subsequent operation on the resulting + * `PARCBuffer` that requires the mark will abort until the mark + * is set again via `parcBuffer_Mark`. + * + * @param [in,out] buffer A pointer to a `PARCBuffer` instance. + * + * @return The value of @p buffer. + * + * Example: + * @code + * { + * parcBuffer_Clear(buffer); + * } + * @endcode + */ +PARCBuffer *parcBuffer_Clear(PARCBuffer *buffer); + +/** + * Determine if two `PARCBuffer` instances are equal. + * + * The following equivalence relations on non-null `PARCBuffer` instances are maintained: * + * * It is reflexive: for any non-null reference value x, `parcBuffer_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `parcBuffer_Equals(x, y)` must return true if and only if + * `parcBuffer_Equals(y x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcBuffer_Equals(x, y)` returns true and + * `parcBuffer_Equals(y, z)` returns true, + * then `parcBuffer_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `parcBuffer_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `parcBuffer_Equals(x, NULL)` must return false. + * + * + * @param [in] x A pointer to a `PARCBuffer` instance. + * @param [in] y A pointer to a `PARCBuffer` instance. + * + * @return true `PARCBuffers` x and y are equal. + * @return false `PARCBuffers` x and y are not equal. + * + * Example: + * @code + * { + * PARCBuffer *bufferA = parcBuffer_Allocate(10); + * PARCBuffer *bufferB = parcBuffer_Allocate(10); + * + * if (parcBuffer_Equals(bufferA, bufferB)) { + * printf("Buffers are equal.\n"); + * } else { + * printf("Buffers are NOT equal.\n"); + * } + * + * parcBuffer_Release(&bufferA); + * parcBuffer_Release(&bufferB); + * } + * @endcode + * + * @see parcBuffer_HashCode + */ +bool parcBuffer_Equals(const PARCBuffer *x, const PARCBuffer *y); + +/** + * Compares instance a with instance b for order. + * + * Returns a negative integer, zero, or a positive integer as instance + * a is less than, equal to, or greater than instance b. + * + * The buffer's position, limit, and mark are not modified. + * + * @param [in] a A pointer to the first instance of `PARCBuffer`. + * @param [in] b A pointer to the second instance of `PARCBuffer`. + * + * @return <0 Instance a is less than instance b. + * @return 0 Instance a and instance b compare the same. + * @return >0 Instance a is greater than instance b. + * + * Example: + * @code + * { + * PARCBuffer *bufferA = parcBuffer_Allocate(10); + * PARCBuffer *bufferB = parcBuffer_Allocate(10); + * + * if (parcBuffer_Compare(bufferA, bufferB) == 0) { + * printf("Buffers are equal.\n"); + * } + * + * parcBuffer_Release(&bufferA); + * parcBuffer_Release(&bufferB); + * } + * @endcode + * + * @see parcBuffer_Equals + */ +int parcBuffer_Compare(const PARCBuffer *a, const PARCBuffer *b); + +/** + * Return a pointer to the {@link PARCByteArray} that backs this buffer. + * + * If this `PARCBuffer` has a capacity of zero, + * there is no array of bytes and this function returns NULL. + * + * Modifications to the contents of the `PARCByteArray` will visible to the given + * `PARCBuffer` and vice-versa. + * + * The origin of the given `PARCBuffer` may not be the same as the origin of the underlying + * `PARCByteArray`. + * Use {@link parcBuffer_ArrayOffset} to obtain the origin of the given `PARCBuffer` + * relative to the origin of the underlying `PARCByteArray` + * + * The caller must obtain its own reference to the `PARCByteArray` if it intends to store it elsewhere. + * + * Note: Many hard to find bugs can be caused by using this function. + * Use the functions provided to manipulate the `PARCBuffer` and its contents. + * + * @param [in] buffer A `PARCBuffer` pointer. + * + * @return NULL There is no `PARCByteArray` backing the given `PARCBuffer` (no capacity). + * @return non-NULL The pointer to the `PARCByteArray` for the given `PARCBuffer`. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * + * PARCByteArray *array = parcBuffer_Array(buffer); + * + * parcBuffer_Release(&buffer); + * } + * @endcode + * + * @see parcByteArray_Acquire + */ +PARCByteArray *parcBuffer_Array(const PARCBuffer *buffer); + +/** + * Create an independent copy the given `PARCBuffer` + * + * A new buffer is created as a complete copy of the original. + * + * @param [in] buffer A pointer to a `PARCBuffer` instance. + * + * @return NULL Memory could not be allocated. + * @return non-NULL A pointer to a new `PARCBuffer` instance. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * parcBuffer_PutUint8(buffer, (uint8_t)'A'); + * + * PARCBuffer *copy = parcBuffer_Copy(buffer); + * + * parcBuffer_Release(©); + * parcBuffer_Release(&buffer); + * } + * @endcode + * + */ +PARCBuffer *parcBuffer_Copy(const PARCBuffer *buffer); + +/** + * Creates a new buffer that shares the original buffer's content. + * + * The content of the new buffer will be that of this buffer. + * Changes to the buffer's content will be visible in both buffers, + * however the two buffers' position, limit, and mark values will be independent. + * + * The new buffer's capacity, limit, position, and mark values will be identical to those of the original buffer. + * + * @param [in] original The orignal PARCBuffer instance that will be duplicated. + * + * @return NULL Memory could not be allocated. + * @return non-NULL A pointer to ta valid `PARCBuffer` instance. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * parcBuffer_Duplicate(buffer2); + * + * parcBuffer_Release(&buffer); + * parcBuffer_Release(&buffer2); + * } + * @endcode + */ +PARCBuffer *parcBuffer_Duplicate(const PARCBuffer *original); + +/** + * Returns the offset within this buffer's backing {@link PARCByteArray} of the first element. + * + * Buffer position <i>p</i> corresponds to array index <i>p + arrayOffset()</i>. + * + * @param [in] buffer A pointer to a `PARCBuffer` instance. + * + * @return `size_t` The offset within this `PARCBuffer`'s array of the first element of the buffer + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * parcBuffer_PutUint8(buffer, (uint8_t)'A'); + * + * size_t arrayOffset = parcBuffer_ArrayOffset(buffer); + * // offset will be 0 since the contents of the buffer start at the beginning + * + * parcBuffer_Release(&buffer); + * } + * @endcode + */ +size_t parcBuffer_ArrayOffset(const PARCBuffer *buffer); + +/** + * Rewinds this `PARCBuffer`: The position is set to zero and the mark is invalidated. + * + * The mark is made invalid and any subsequent operation on the resulting + * `PARCBuffer` that requires the mark will abort until the mark + * is set again via `parcBuffer_Mark`. + * + * @param [in,out] buffer A pointer to a `PARCBuffer` instance. + * + * @return The given `PARCBuffer` pointer. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * parcBuffer_PutUint8(buffer, (uint8_t)'A'); + * + * parcBuffer_Rewind(buffer); // bring it back to zero + * + * parcBuffer_Release(&buffer); + * } + * @endcode + */ +PARCBuffer *parcBuffer_Rewind(PARCBuffer *buffer); + +/** + * Resets the given `PARCBuffer`'s position to the previously-marked position. + * + * Invoking this method neither changes nor invalidates the mark's value. + * + * @param [in,out] buffer A pointer to a `PARCBuffer` instance. + * + * @return The given `PARCBuffer` pointer. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * parcBuffer_PutUint8(buffer, (uint8_t)'A'); + * + * buffer = parcBuffer_Reset(buffer); + * + * parcBuffer_Release(&buffer); + * } + * @endcode + */ +PARCBuffer *parcBuffer_Reset(PARCBuffer *buffer); + +/** + * Return the given `PARCBuffer`'s limit. + * + * A buffer's limit is the index of the first element that should not be read or written. + * A buffer's limit is never negative and is never greater than its capacity. + * + * @param [in] buffer A pointer to a `PARCBuffer` instance. + * + * @return The given `PARCBuffer`'s limit. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * parcBuffer_PutUint8(buffer, (uint8_t)'A'); + * + * size_t limit = parcBuffer_Limit(buffer); + * // limit will be 10 + * + * parcBuffer_Release(&buffer); + * } + * @endcode + */ +size_t parcBuffer_Limit(const PARCBuffer *buffer); + +/** + * Sets this buffer's mark at its position. + * + * @param [in,out] buffer A pointer to a `PARCBuffer` instance. + * + * @return The value of @p buffer. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * parcBuffer_PutUint8(buffer, (uint8_t)'A'); + * parcBuffer_Mark(buffer); + * // since the position was 0, the mark remains at 0 + * + * ... + * + * parcBuffer_Release(&buffer); + * } + * @endcode + */ +PARCBuffer *parcBuffer_Mark(PARCBuffer *buffer); + +/** + * Sets this `PARCBuffer`'s limit. + * + * If the position is larger than the new limit then it is set to the new limit. + * + * If the mark is defined and larger than the new limit then the mark is invalidated and + * any subsequent operation that requires the mark will abort until the mark + * is set again via `parcBuffer_Mark` + * + * @param [in,out] buffer A pointer to a `PARCBuffer` instance. + * @param newLimit The new limit value; must be no larger than this `PARCBuffer`'s capacity. + * + * @return The given `PARCBuffer` pointer. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * parcBuffer_PutUint8(buffer, (uint8_t)'A'); + * + * parcBuffer_SetLimit(buffer, 8); + * + * size_t limit = parcBuffer_Limit(buffer); + * size_t capacity = parcBuffer_Capacity(buffer); + * // capacity is 10, limit is 8 + * + * ... + * + * parcBuffer_Release(&buffer); + * } + * @endcode + */ +PARCBuffer *parcBuffer_SetLimit(PARCBuffer *buffer, size_t newLimit); + +/** + * Return a pointer to buffer memory starting the buffer's current position. + * + * The @p length parameter must be less than or equal to the remaining bytes in the buffer + * and has no effect on the return value, + * except that if the buffer's position is equal to the limit, then this traps with OutOfBounds + * + * The current position of the buffer is advanced by @p length bytes. + * It is acceptable for the @p length parameter to be zero, + * thereby causing the current position to remain unchanged. + * + * This does not guarantee any particular memory alignment. + * Therefore, it is possible to obtain a pointer to memory that cannot be accessed + * as a native type because of CPU architecture alignment requirements. + * + * The function returns a pointer to contiguous memory within a `PARCBuffer`, + * but does not acquire a reference to the `PARCBuffer` instance, + * the underlying {@link PARCByteArray}, nor the actual memory array. + * If the {@link PARCBuffer} or the underlying {@link PARCByteArray} is released finally, + * the result from a previous call to `parcBuffer_Overlay` will point to undefined values. + * + * @param [in,out] buffer A pointer to a `PARCBuffer` instance. + * @param [in] length The number of bytes to advance the buffer's position. + * + * @return non-NULL A pointer to memory. + * + * Example: + * @code + * { + * char *expected = "Hello World"; + * struct timeval theTime = { .tv_sec = 123, .tv_usec = 456}; + * + * PARCBuffer *buffer = parcBuffer_Allocate(sizeof(uint16_t) + strlen(expected) + sizeof(theTime)); + * + * parcBuffer_PutUint16(buffer, strlen(expected)); + * parcBuffer_PutUint8(buffer, expected, strlen(expected)); + * parcBuffer_PutUint8(buffer, &theTime, sizeof(theTime)); + * parcBuffer_Flip(); + * + * uint16_t length = parcBuffer_GetUint16(buffer); + * char *actual = parcBuffer_Overlay(buffer, length); + * struct timeval *tm = parcBuffer_Overlay(buffer, sizeof(struct timeval)); + * } + * @endcode + */ +void *parcBuffer_Overlay(PARCBuffer *buffer, size_t length); + +/** + * Return the given `PARCBuffer`'s position. + * + * A buffer's position is the index of the next element to be read or written. + * A buffer's position is never negative and is never greater than its limit. + * + * @param [in] buffer A pointer to a `PARCBuffer` instance. + * @return The given `PARCBuffer`'s position. + * + * Example: + * @code + * { + * size_t currentPosition = parcBuffer_Position(buffer); + * } + * @endcode + * + * @see parcBuffer_SetPosition + */ +size_t parcBuffer_Position(const PARCBuffer *buffer); + +/** + * Set the given `PARCBuffer`'s position. + * + * A buffer's position is the index of the next element to be read or written. + * A buffer's position is never negative and is never greater than its limit. + * + * If the mark is defined and larger than the new position then the mark + * is invalidated and any subsequent operation on the resulting + * `PARCBuffer` that requires the mark will abort until the mark + * is set again via `parcBuffer_Mark`. + * + * @param [in,out] buffer A pointer to a `PARCBuffer` instance. + * @param [in] newPosition The buffer's new position which must be less than or equal to the current limit. + * + * @return The given `PARCBuffer`'s position. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * parcBuffer_SetPosition(buffer, 5); + * parcBuffer_Remaining(buffer); // Returns 5. + * } + * @endcode + * + * @see parcBuffer_Limit + */ +PARCBuffer *parcBuffer_SetPosition(PARCBuffer *buffer, size_t newPosition); + +/** + * Returns the number of elements between the current position and the limit. + * + * @param [in] buffer A pointer to a `PARCBuffer` instance. + * + * @return The number of elements remaining in this `PARCBuffer`. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * parcBuffer_SetPosition(buffer, 5); + * parcBuffer_Remaining(buffer); // Returns 5. + * } + * @endcode + */ +size_t parcBuffer_Remaining(const PARCBuffer *buffer); + +/** + * Tells whether there are any elements between the current position and the limit. + * + * @param [in] buffer A pointer to a `PARCBuffer` instance. + * + * @return true The `PARCBuffer` contains at least one more element. + * @return false The `PARCBuffer` does not contain any more elements. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * parcBuffer_SetPosition(buffer, 5); + * bool hasRemaining = parcBuffer_HasRemaining(buffer); // returns true since #remaining = 5 + * } + * @endcode + */ +bool parcBuffer_HasRemaining(const PARCBuffer *buffer); + +/** + * Creates a new byte buffer whose content is a shared subsequence of this buffer's content. + * + * The content of the new buffer will start at this buffer's current position. + * Changes to this buffer's content will be visible in the new buffer, + * and vice versa; + * the two buffers' position, limit, + * and mark values will be independent. + * + * The new buffer's position will be zero, + * its capacity and its limit will be the number of bytes remaining in this buffer, + * and its mark will be undefined. + * + * @param [in] original A pointer to a `PARCBuffer` instance. + * + * @return non-NULL A pointer to a new `PARCBuffer` whose content is a shared subsequence of the original buffer's content. + * @return NULL Memory could not be allocated. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * parcBuffer_SetPosition(buffer, 5); + * parcBuffer_SetLimit(buffer, 8); + * + * PARCBuffer *slice = parcBuffer_Slice(buffer); + * // the slice will be the subset of bytes 5,6,7, and will + * // have limit and capacity of 3 (= 8 - 5) + * + * ... + * + * parcBuffer_Release(&buffer); + * parcBuffer_Release(&slice); + * } + * @endcode + */ +PARCBuffer *parcBuffer_Slice(const PARCBuffer *original); + +/** + * Set the limit to the current position, + * then set the position to zero. + * If the mark is defined, it is invalidated. + * + * Any subsequent operation that requires the mark will abort until the mark + * is set again via `parcBuffer_Mark`. + * + * @param [in,out] buffer A pointer to a `PARCBuffer` instance. + * + * @return The same value as @p buffer. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * parcBuffer_PutByte(buffer, 'X'); + * parcBuffer_Flip(buffer); + * uint8_t actual = parcBuffer_GetUint8(buffer); + * + * ... + * parcBuffer_Release(&buffer); + * } + * @endcode + */ +PARCBuffer *parcBuffer_Flip(PARCBuffer *buffer); + +/** + * Get the single `uint8_t` at the index specified. + * + * The buffer's position is not modified. + * + * @param [in] buffer A pointer to a `PARCBuffer` instance. + * @param [in] index The index into the @p buffer to find the `uint8_t`. + * + * @return The `uint8_t` value at @p index. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * parcBuffer_PutByte(buffer, 'X'); + * uint8_t actual = parcBuffer_GetAtIndex(buffer, 0); + * // actual == (uint8_t) 'X' + * + * ... + * parcBuffer_Release(&buffer); + * } + * @endcode + */ +uint8_t parcBuffer_GetAtIndex(const PARCBuffer *buffer, size_t index); + +/** + * Read the unsigned 8-bit value at the buffer's current position, + * and then increment the position by 1. + * + * @param [in] buffer The pointer to a `PARCBuffer` instance containing the `uint8_t` value. + * + * @return The `uint8_t` at the buffer's current position + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * parcBuffer_PutByte(buffer, 'X'); + * parcBuffer_Flip(buffer); + * uint8_t actual = parcBuffer_GetUint8(buffer); + * + * ... + * parcBuffer_Release(&buffer); + * } + * @endcode + * + */ +uint8_t parcBuffer_GetUint8(PARCBuffer *buffer); + +/** + * Read the unsigned 16-bit value in network order at the buffer's current position, + * and then increment the position by 2. + * + * @param [in,out] buffer The pointer to the `PARCBuffer` instance containing the value. + * + * @return The `uint16_t` at the buffer's current position. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * (buffer, 0x1234); + * parcBuffer_Flip(buffer); + * uint16_t actual = parcBuffer_GetUint16(buffer); + * } + * @endcode + * + * @see parcBuffer_Overlay + */ +uint16_t parcBuffer_GetUint16(PARCBuffer *buffer); + +/** + * Read the unsigned 32-bit value in network order at the buffer's current position, + * and then increment the position by the 4. + * + * @param [in,out] buffer The pointer to the instance of `PARCBuffer` containing the value. + * + * @return The `uint32_t` at the buffer's current position. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * parcBuffer_PutUint32(buffer, 0x12345678); + * parcBuffer_Flip(buffer); + * uint32_t actual = parcBuffer_GetUint32(buffer); + * } + * @endcode + * + * @see parcBuffer_Overlay + */ +uint32_t parcBuffer_GetUint32(PARCBuffer *buffer); + +/** + * Read the unsigned 64-bit value in network order at the buffer's current position, + * and then increment the position by 8. + * + * @param [in,out] buffer The pointer to the instance of `PARCBuffer` containing the value. + * + * @return The `uint64_t` at the buffer's current position. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * parcBuffer_PutUint64(buffer, 0x12345678); + * parcBuffer_Flip(buffer); + * uint64_t actual = parcBuffer_GetUint64(buffer); + * } + * @endcode + * + * @see parcBuffer_Overlay + */ +uint64_t parcBuffer_GetUint64(PARCBuffer *buffer); + +/** + * Read an array of length bytes from the given PARCBuffer, copying them to an array. + * + * The buffer's position is incremented by @p length. + * + * @param [in,out] buffer The pointer to the instance of `PARCBuffer` containing the `uint8_t` value. + * @param [in] length The number of `uint8_t` elements to get. + * @param [in] array The `uint8_t` array to receive @p length bytes. + * + * @return A pointer to the given `PARCBuffer` instance + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(5); + * parcBuffer_PutUint8(buffer, 'A'); + * parcBuffer_PutUint8(buffer, 'B'); + * parcBuffer_PutUint8(buffer, 'C'); + * + * uint8_t array[3]; + * parcBuffer_GetBytes(buffer, 3, array); + * // array[0] == 'A' + * // array[1] == 'B' + * // array[2] == 'C' + * } + * @endcode + * + * @see parcBuffer_Overlay + */ +PARCBuffer *parcBuffer_GetBytes(PARCBuffer *buffer, size_t length, uint8_t *array); + +/** + * Insert an unsigned 8-bit value into the given `PARCBuffer` at the current position. + * + * Advance the current position by 1. + * + * @param [in,out] buffer A pointer to the `PARCBuffer` instance. + * @param [in] value The value to be inserted into the`PARCBuffer` instance at the current position. + * @return The `PARCBuffer` + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * parcBuffer_PutByte(buffer, 'X'); + * } + * @endcode + */ +PARCBuffer *parcBuffer_PutUint8(PARCBuffer *buffer, uint8_t value); + +/** + * Insert an unsigned 16-bit value into the given `PARCBuffer` at the current position, + * in big-endian, network-byte-order. + * + * Advance the current position by 2. + * + * @param [in,out] buffer A pointer to the `PARCBuffer` instance. + * @param [in] value The value to be inserted + * @return The pointer to `PARCBuffer` + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * parcBuffer_PutUint16(buffer, 0x1234); + * } + * @endcode + */ +PARCBuffer *parcBuffer_PutUint16(PARCBuffer *buffer, uint16_t value); + +/** + * Insert an unsigned 32-bit value into the given `PARCBuffer` at the current position, + * in big-endian, network-byte-order. + * + * Advance the current position by 4. + * + * @param [in,out] buffer A pointer to the `PARCBuffer` instance. + * @param [in] value The value to be inserted + * @return The `PARCBuffer` + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * parcBuffer_PutUint32(buffer, 0x12345678); + * } + * @endcode + */ +PARCBuffer *parcBuffer_PutUint32(PARCBuffer *buffer, uint32_t value); + +/** + * Insert an unsigned 64-bit value into the given `PARCBuffer` at the current position, + * in big-endian, network-byte-order. + * + * Advance the current position by 8. + * + * @param [in,out] buffer A pointer to the `PARCBuffer` instance. + * @param [in] value The value to be inserted + * @return The `PARCBuffer` + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * parcBuffer_PutUint64(buffer, 0x1234); + * } + * @endcode + */ +PARCBuffer *parcBuffer_PutUint64(PARCBuffer *buffer, uint64_t value); + +/** + * Insert unsigned 8-bit value to the given `PARCBuffer` at given index. + * + * The buffer's position is unchanged. + * + * @param [in,out] buffer A pointer to the `PARCBuffer` instance. + * @param [in] index The index at which to insert @p value + * @param [in] value The value to be inserted + * + * @return The value of @p buffer. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * parcBuffer_PutAtIndex(buffer, 3, 'X'); + * } + * @endcode + */ +PARCBuffer *parcBuffer_PutAtIndex(PARCBuffer *buffer, size_t index, uint8_t value); + +/** + * Copy `arrayLength` bytes from the given array into the `PARCBuffer`. + * + * The position is incremented by `arrayLength` + * + * @param [in] buffer A pointer to the `PARCBuffer` instance. + * @param [in] arrayLength The number of bytes to copy into the buffer. + * @param [in] array A pointer to the array of bytes. + * + * @return The value of @p buffer. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(5); + * + * uint8_t array[3]; + * array[0] = 'A'; + * array[1] = 'B'; + * array[2] = 'C'; + * + * parcBuffer_PutArray(buffer, 3, array); + * // the buffer will now contain ['A','B','C'] at indices 0,1,2 + * } + * @endcode + */ +PARCBuffer *parcBuffer_PutArray(PARCBuffer *buffer, size_t arrayLength, const uint8_t *array); + +/** + * Copy the contents of the given nul-terminated C string into a PARCBuffer, including the terminating nul byte. + * + * The position is incremented by the length of the string plus 1. + * + * @param [in] buffer A pointer to the `PARCBuffer` instance. + * @param [in] string A pointer to nul-terminated C string. + * + * @return The value of @p buffer. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(5); + * + * char *string = "ABC"; + * + * parcBuffer_PutCString(buffer, string); + * // the buffer will now contain ['A','B','C', 0] at indices 0, 1, 2, and 3 + * } + * @endcode + */ +PARCBuffer *parcBuffer_PutCString(PARCBuffer *buffer, const char *string); + +/** + * Put the contents of a `PARCBuffer` into another. + * + * @param [in] buffer A pointer to the destination `PARCBuffer` instance. + * @param [in] source A pointer to the source `PARCBuffer` instance. + * + * @return The value of @p result. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * PARCBuffer *insertee = parcBuffer_AllocateCString("Hello"); + * parcBuffer_PutBuffer(buffer, insertee); + * // buffer will now contain "Hello" in the first 5 byte indices + * } + * @endcode + */ +PARCBuffer *parcBuffer_PutBuffer(PARCBuffer *buffer, const PARCBuffer *source); + +/** + * Returns a hash code value for the given instance. + * + * The hash code of a `PARCBuffer` depends only upon its remaining elements from the current position to the limit. + * + * Because `PARCBuffer` hash codes are content-dependent, be careful when using them as keys in `PARCHashMap` + * and other similar data structures unless it is known that their contents will not change. + * + * The general contract of `HashCode` is: + * + * Whenever it is invoked on the same instance more than once during an execution of an application, + * the `HashCode` function must consistently return the same value, + * provided no information used in a corresponding {@link parcByteArray_Equals} + * comparisons on the instance is modified. + * + * This value need not remain consistent from one execution of an application to another execution of the same application. + * If two instances are equal according to the {@link parcByteArray_Equals} method, + * then calling the {@link parcBuffer_HashCode} method on each of the two instances must produce the same integer result. + * + * It is not required that if two instances are unequal according to the {@link parcBuffer_Equals} function, + * then calling the `parcBuffer_HashCode` + * method on each of the two objects must produce distinct integer results. + * + * @param [in] buffer A pointer to the `PARCBuffer` instance. + * + * @return The hashcode for the given instance. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * uint32_t hashValue = parcBuffer_HashCode(buffer); + * parcBuffer_Release(&buffer); + * } + * @endcode + * + * @see parcByteArray_HashCode + */ +PARCHashCode parcBuffer_HashCode(const PARCBuffer *buffer); + +/** + * Return the position of the first `uint8_t` value that matches the given byte. + * + * If the value does not exist between the current position and the limit, + * return `SIZE_MAX` (<stdint.h>). + * + * @param [in] buffer A pointer to a `PARCBuffer` instance. + * @param [in] byte The byte to search for within the buffer. + * + * @return The index of the first byte equal to `byte`, or `SIZE_MAX` (<stdint.h>) + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Wrap("Hello World", 10, 0, 10); + * + * size_t ePosition = parcBuffer_FindUint8(buffer, 'e'); + * + * // ePosition is equal to 1. + * + * size_t xPosition = parcBuffer_FindUint8(buffer, 'x'); + * + * // ePosition is equal to SIZE_MAX. + * } + * @endcode + */ +size_t parcBuffer_FindUint8(const PARCBuffer *buffer, uint8_t byte); + +/** + * Produce a null-terminated string representation of the specified `PARCBuffer` + * from the current position to the limit. + * The buffer's position is not changed. + * + * The result must be freed by the caller via {@link parcMemory_Deallocate}. + * + * @param [in] buffer A pointer to the instance. + * + * @return NULL Cannot allocate memory. + * @return non-NULL A pointer to an allocated, null-terminated C string that must be deallocated via {@link parcMemory_Deallocate}. + * + * Example: + * @code + * { + * char *helloWorld = "Hello World"; + * PARCBuffer *instance = parcBuffer_Wrap(helloWorld, strlen(helloWorld), 0, strlen(helloWorld)); + * + * char *string = parcBuffer_ToString(instance); + * + * if (string != NULL) { + * printf("Hello: %s\n", string); + * parcMemory_Deallocate((void **)&string); + * } else { + * printf("Cannot allocate memory\n"); + * } + * + * parcBuffer_Release(&instance); + * } + * @endcode + * + * @see parcBuffer_Display + */ +char *parcBuffer_ToString(const PARCBuffer *buffer); + +/** + * Print a human readable representation of the given `PARCBuffer`. + * + * @param [in] indentation The level of indentation to use to pretty-print the output. + * @param [in] buffer A pointer to the instance to display. + * + * Example: + * @code + * { + * PARCBuffer *instance = parcBuffer_Create(); + * + * parcBuffer_Display(instance, 0); + * + * parcBuffer_Release(&instance); + * } + * @endcode + */ +void parcBuffer_Display(const PARCBuffer *buffer, int indentation); + +/** + * Return a null-terminated string containing the hex-byte representation of the given `PARCBuffer`. + * + * The result must be freed by the caller via `parcMemory_Deallocate()`. + * + * @param [in] buffer A pointer to a `PARCBuffer` instance. + * + * @return NULL Cannot allocate memory. + * @return non-NULL A pointer to an allocated, null-terminated C string that must be deallocated via {@link parcMemory_Deallocate}. + * + * Example: + * @code + * { + * PARCBuffer *instance = parcBuffer_Create(); + * + * char *hexString = parcBuffer_ToHexString(instance); + * parcMemory_Deallocate((void **)&hexString); + * + * parcBuffer_Release(&instance); + * } + * @endcode + * + * @see parcMemory_Deallocate + */ +char *parcBuffer_ToHexString(const PARCBuffer *buffer); + +/** + * Advance the position of the given buffer to the first byte that is not in the array @p bytesToSkipOver. + * + * The position will not exceed the PARCBuffer's limit. + * + * @param [in,out] buffer A pointer to a `PARCBuffer` instance. + * @param [in] length Length of the byte array that includes skip bytes + * @param [in] bytesToSkipOver A null-terminated array of bytes to skip. + * + * @return true The `PARCBuffer`'s position was updated. + * @return false The `PARCBuffer`'s position reached the limit. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_WrapCString("Hello World"); + * uint8_t skipOverBytes[] = { 'H', 0 }; + * + * bool actual = parcBuffer_SkipOver(buffer, 1, skipOverBytes); + * // the buffer position will now be 1, as it skipped over the 'H' byte in the array + * // actual will be true + * } + * @endcode + * + * @see parcBuffer_SkipTo + */ +bool parcBuffer_SkipOver(PARCBuffer *buffer, size_t length, const uint8_t *bytesToSkipOver); + +/** + * Advance the position of the given buffer to the first byte that is in the array @p bytesToSkipTo. + * + * The position will not exceed the PARCBuffer's limit. + * + * @param [in,out] buffer A pointer to a `PARCBuffer` instance. + * @param [in] length Length of the byte array that includes skip bytes + * @param [in] bytesToSkipTo A null-terminated array of bytes to find. + * + * @return true The PARCBuffer's position is at the first byte matched. + * @return false The PARCBuffer's position reached the limit. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_WrapCString("Hello World"); + * uint8_t skipOverBytes[] = { 'l', 0 }; + * + * bool actual = parcBuffer_SkipTo(buffer, 1, skipOverBytes); + * // the buffer position will now be set to the index of the first 'l' byte in the underlying array + * // actual will be true + * } + * @endcode + * + * @see parcBuffer_SkipOver + */ +bool parcBuffer_SkipTo(PARCBuffer *buffer, size_t length, const uint8_t *bytesToSkipTo); + +/** + * Return the byte at the given `PARCBuffers'` current position, without modifying the position. + * + * @param [in] buffer A pointer to a `PARCBuffer` instance. + * + * @return The byte at the given `PARCBuffers'` current position + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_AllocateCString("Hello"); + * uint8_t byte = parcBuffer_PeekByte(1); + * // byte == (uint8_t) 'e'; + * } + * @endcode + */ +uint8_t parcBuffer_PeekByte(const PARCBuffer *buffer); + +/** + * Parse an ASCII representation of a hexadecimal number in the given `PARCBuffer` + * + * The number may be prefixed with the characters '0', 'x'. + * The buffer's position will be left at the first non-parsable character. + * + * Overflow is not checked. + * + * @param [in] buffer A pointer to a valid `PARCBuffer` instance. + * + * @return A uint64_t of the hexadecimal number. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_WrapCString("0x10"); + * uint64_t value = parcBuffer_ParseHexNumber(buffer); + * + * parcBuffer_Release(&buffer); + * } + * @endcode + */ +uint64_t parcBuffer_ParseHexNumber(PARCBuffer *buffer); + +/** + * Parse an ASCII representation of a unsigned decimal number in the given `PARCBuffer` + * + * The buffer's position will be left at the first non-parsable character. + * + * Overflow is not checked. + * + * @param [in] buffer A pointer to a valid `PARCBuffer` instance. + * + * @return A uint64_t of the decimal number. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_WrapCString("100"); + * uint64_t value = parcBuffer_ParseDecimalNumber(buffer); + * + * parcBuffer_Release(&buffer); + * } + * @endcode + */ +uint64_t parcBuffer_ParseDecimalNumber(PARCBuffer *buffer); + +/** + * Parse an ASCII representation of a unsigned decimal number or a hexadecimal number in the given `PARCBuffer` + * + * The buffer's position will be left at the first non-parsable character. + * + * Overflow is not checked. + * + * @param [in] buffer A pointer to a valid `PARCBuffer` instance. + * + * @return A uint64_t of the number. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_WrapCString("100"); + * uint64_t value = parcBuffer_ParseNumeric(buffer); + * + * parcBuffer_Release(&buffer); + * } + * @endcode + */ +uint64_t parcBuffer_ParseNumeric(PARCBuffer *buffer); +#endif // libparc_parc_Buffer_h diff --git a/libparc/parc/algol/parc_BufferChunker.c b/libparc/parc/algol/parc_BufferChunker.c new file mode 100644 index 00000000..f3a4ab67 --- /dev/null +++ b/libparc/parc/algol/parc_BufferChunker.c @@ -0,0 +1,261 @@ +/* + * 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 <stdio.h> +#include <string.h> +#include <sys/time.h> + +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_Buffer.h> +#include <parc/algol/parc_Memory.h> + +#include <parc/algol/parc_BufferChunker.h> + +PARCChunkerInterface *PARCBufferChunkerAsChunker = &(PARCChunkerInterface) { + .ForwardIterator = (void *(*)(const void *))parcBufferChunker_ForwardIterator, + .ReverseIterator = (void *(*)(const void *))parcBufferChunker_ReverseIterator, + .GetChunkSize = (size_t (*)(const void *))parcBufferChunker_GetChunkSize +}; + +struct _parc_chunker_state { + size_t chunkNumber; + int direction; + bool atEnd; + size_t position; + size_t nextChunkSize; +}; + +typedef struct _parc_chunker_state _ChunkerState; + +struct parc_buffer_chunker { + // State + size_t chunkSize; + + // Container for the data to be parsed + PARCBuffer *data; + + // The current element of the iterator + PARCBuffer *currentElement; +}; + +static void +_destroy(PARCBufferChunker **chunkerP) +{ + if ((*chunkerP)->data != NULL) { + parcBuffer_Release(&(*chunkerP)->data); + } + + if ((*chunkerP)->currentElement != NULL) { + parcBuffer_Release(&(*chunkerP)->currentElement); + } +} + +static void * +_InitForward(PARCBufferChunker *chunker) +{ + _ChunkerState *state = parcMemory_Allocate(sizeof(_ChunkerState)); + + state->chunkNumber = 0; + state->direction = 0; + state->position = 0; + state->atEnd = false; + + if (parcBuffer_Remaining(chunker->data) < chunker->chunkSize) { + state->nextChunkSize = parcBuffer_Remaining(chunker->data); + } else { + state->nextChunkSize = chunker->chunkSize; + } + + return state; +} + +static void * +_InitReverse(PARCBufferChunker *chunker) +{ + _ChunkerState *state = parcMemory_Allocate(sizeof(_ChunkerState)); + + state->chunkNumber = 0; + state->direction = 1; + state->atEnd = false; + + if (parcBuffer_Remaining(chunker->data) < chunker->chunkSize) { + state->position = 0; + state->nextChunkSize = parcBuffer_Remaining(chunker->data); + } else { + state->position = parcBuffer_Remaining(chunker->data) - chunker->chunkSize; + state->nextChunkSize = chunker->chunkSize; + } + + return state; +} + +static bool +_ccnxChunker_HasNext(PARCBufferChunker *chunker, void *voidstate) +{ + _ChunkerState *state = (_ChunkerState *) voidstate; + return !state->atEnd; +} + +static void +_advanceStateForward(PARCBufferChunker *chunker, _ChunkerState *state) +{ + state->position += state->nextChunkSize; + + size_t remaining = parcBuffer_Remaining(chunker->data); + + if (remaining == 0) { + state->atEnd = true; + } else if (remaining > chunker->chunkSize) { + state->nextChunkSize = chunker->chunkSize; + } else { + state->nextChunkSize = remaining; + } +} + +static void +_advanceStateBackward(PARCBufferChunker *chunker, _ChunkerState *state) +{ + // Previous chunk size + size_t chunkSize = state->nextChunkSize; + if (chunkSize != chunker->chunkSize || state->position == 0) { + state->atEnd = true; + } else { + if (state->position < chunkSize) { + state->nextChunkSize = state->position; // on next read, go to the current position + state->position = 0; // we reached the beginning + } else { + state->position -= chunkSize; + } + } +} + +static void +_advanceState(PARCBufferChunker *chunker, _ChunkerState *state) +{ + state->chunkNumber++; + + if (state->direction == 0) { + _advanceStateForward(chunker, state); + } else { + _advanceStateBackward(chunker, state); + } +} + +static void * +_ccnxChunker_NextFromBuffer(PARCBufferChunker *chunker, _ChunkerState *state) +{ + size_t chunkSize = state->nextChunkSize; + + parcBuffer_SetPosition(chunker->data, state->position); + PARCBuffer *slice = parcBuffer_CreateFromArray(parcBuffer_Overlay(chunker->data, chunkSize), chunkSize); + slice = parcBuffer_Flip(slice); + + _advanceState(chunker, state); + + return slice; +} + +static void * +_ccnxChunker_Next(PARCBufferChunker *chunker, void *state) +{ + PARCBuffer *buffer = _ccnxChunker_NextFromBuffer(chunker, state); + + if (chunker->currentElement != NULL) { + parcBuffer_Release(&chunker->currentElement); + } + if (buffer != NULL) { + chunker->currentElement = parcBuffer_Acquire(buffer); + } + + return state; +} + +static void * +_ccnxChunker_GetElement(PARCBufferChunker *chunker, void *state) +{ + return chunker->currentElement; +} + +static void +_ccnxChunker_Finish(PARCBufferChunker *chunker, void *state) +{ + _ChunkerState *thestate = (_ChunkerState *) state; + parcMemory_Deallocate(&thestate); +} + +static void +_ccnxChunker_AssertValid(const void *state) +{ + // pass +} + +parcObject_ExtendPARCObject(PARCBufferChunker, _destroy, NULL, NULL, NULL, NULL, NULL, NULL); +parcObject_ImplementAcquire(parcBufferChunker, PARCBufferChunker); +parcObject_ImplementRelease(parcBufferChunker, PARCBufferChunker); + +PARCBufferChunker * +parcBufferChunker_Create(PARCBuffer *data, size_t chunkSize) +{ + PARCBufferChunker *chunker = parcObject_CreateInstance(PARCBufferChunker); + + if (chunker != NULL) { + chunker->chunkSize = chunkSize; + chunker->data = parcBuffer_Acquire(data); + chunker->currentElement = NULL; + } + + return chunker; +} + +PARCIterator * +parcBufferChunker_ForwardIterator(const PARCBufferChunker *chunker) +{ + PARCIterator *iterator = parcIterator_Create((void *) chunker, + (void *(*)(PARCObject *))_InitForward, + (bool (*)(PARCObject *, void *))_ccnxChunker_HasNext, + (void *(*)(PARCObject *, void *))_ccnxChunker_Next, + NULL, + (void *(*)(PARCObject *, void *))_ccnxChunker_GetElement, + (void (*)(PARCObject *, void *))_ccnxChunker_Finish, + (void (*)(const void *))_ccnxChunker_AssertValid); + + return iterator; +} + +PARCIterator * +parcBufferChunker_ReverseIterator(const PARCBufferChunker *chunker) +{ + PARCIterator *iterator = parcIterator_Create((void *) chunker, + (void *(*)(PARCObject *))_InitReverse, + (bool (*)(PARCObject *, void *))_ccnxChunker_HasNext, + (void *(*)(PARCObject *, void *))_ccnxChunker_Next, + NULL, + (void *(*)(PARCObject *, void *))_ccnxChunker_GetElement, + (void (*)(PARCObject *, void *))_ccnxChunker_Finish, + (void (*)(const void *))_ccnxChunker_AssertValid); + + return iterator; +} + +size_t +parcBufferChunker_GetChunkSize(const PARCBufferChunker *chunker) +{ + return chunker->chunkSize; +} diff --git a/libparc/parc/algol/parc_BufferChunker.h b/libparc/parc/algol/parc_BufferChunker.h new file mode 100755 index 00000000..16f21c49 --- /dev/null +++ b/libparc/parc/algol/parc_BufferChunker.h @@ -0,0 +1,217 @@ +/* + * 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. + */ + +/** + * @file parc_BufferChunker.h + * @ingroup ContentObject + * @brief A BufferChunker is a chunker that segments a PARCBuffer. + * + */ + +#ifndef libparc_parc_BufferChunker_h +#define libparc_parc_BufferChunker_h + +#include <parc/algol/parc_Chunker.h> + +struct parc_buffer_chunker; +/** + * @typedef PARCChunker + * @brief The PARC Chunker + */ +typedef struct parc_buffer_chunker PARCBufferChunker; + +/** + * The mapping of a `PARCBufferChunker` to the generic `PARCChunker`. + */ +extern PARCChunkerInterface *PARCBufferChunkerAsChunker; + +/** + * Create a new chunker to segment data contained in a `PARCBuffer.` + * + * @param [in] data A `PARCBuffer` which contains the data. + * @param [in] chunkSize The size per chunk. + * + * @retval PARCChunker A newly allocated `PARCChunker` + * @retval NULL An error occurred. + * + * Example + * @code + * { + * PARCBuffer *dataToChunk = ... + * PARCChunker *chunker = ccnxChunker_CreateFromBuffer(dataToChunk, 32); + * } + */ +PARCBufferChunker *parcBufferChunker_Create(PARCBuffer *data, size_t chunkSize); + +/** + * Increase the number of references to a `PARCChunker` instance. + * + * Note that new `PARCChunker` is not created, + * only that the given `PARCChunker` reference count is incremented. + * Discard the reference by invoking {@link ccnxChunker_Release}. + * + * @param [in] chunker A pointer to the original `PARCChunker`. + * @return The value of the input parameter @p chunker. + * + * Example: + * @code + * { + * PARCBufferChunker *original = parcBufferChunker_Create(...); + * + * PARCBufferChunker *reference = parcBufferChunker_Acquire(original); + * + * parcBufferChunker_Release(&original); + * parcBufferChunker_Release(&reference); + * } + * @endcode + * + * @see parcBufferChunker_Release + */ +PARCBufferChunker *parcBufferChunker_Acquire(const PARCBufferChunker *chunker); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] chunkerP A pointer to a pointer to the instance to release. + * + * + * Example: + * @code + * { + * ... + * + * PARCBufferChunker *chunker = parcBufferChunker_Acquire(instance); + * + * parcBufferChunker_Release(&chunker); + * } + * @endcode + */ +void parcBufferChunker_Release(PARCBufferChunker **chunkerP); + +/** + * Determine if two `PARCBufferChunker` instances are equal. + * + * The following equivalence relations on non-null `PARCBufferChunker` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, `parcBufferChunker_Equals(x, x)` + * must return true. + * + * * It is symmetric: for any non-null reference values x and y, + * `parcBufferChunker_Equals(x, y)` must return true if and only if + * `parcBufferChunker_Equals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcBufferChunker_Equals(x, y)` returns true and + * `parcBufferChunker_Equals(y, z)` returns true, + * then `parcBufferChunker_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple + * invocations of `parcBufferChunker_Equals(x, y)` consistently return true or + * consistently return false. + * + * * For any non-null reference value x, `parcBufferChunker_Equals(x, NULL)` must + * return false. + * + * @param chunkerA A pointer to a `PARCBufferChunker` instance. + * @param chunkerB A pointer to a `PARCBufferChunker` instance. + * @return true if the two `PARCBufferChunker` instances are equal. + * + * Example: + * @code + * { + * PARCBuffer *dataToChunk = ... + * PARCBufferChunker *chunkerA = parcBufferChunker_Create(dataToChunk, 32); + * PARCBufferChunker *chunkerB = parcBufferChunker_Create(dataToChunk, 32); + * + * bool equals = parcBufferChunker_Equals(chunkerA, chunkerB); + * } + * @endcode + */ +bool parcBufferChunker_Equals(const PARCBufferChunker *chunkerA, const PARCBufferChunker *chunkerB); + +/** + * Return an iterator to traverse the chunks of the underlying data in sequential order. + * + * This function can only be called once per chunker instance since the iterator + * will mutate internal state of the chunker. + * + * @param [in] chunker A `PARCBufferChunker` instance. + * + * @return a `PARCIterator` that traverses the chunks of the underlying data. + * + * Example + * @code + * { + * PARCBuffer *dataToChunk = ... + * PARCBufferChunker *chunker = parcBufferChunker_Create(dataToChunk, 32); + * + * PARCIterator *itr = parcBufferChunker_ForwardIterator(chunker); + * + * // use the iterator to traverse the chunker + * } + * @endcode + */ +PARCIterator *parcBufferChunker_ForwardIterator(const PARCBufferChunker *chunker); + +/** + * Return an iterator to traverse the chunks of the underlying data in sequential order. + * + * This function can only be called once per chunker instance since the iterator + * will mutate internal state of the chunker. + * + * @param [in] chunker A `PARCBufferChunker` instance. + * + * @return a `PARCIterator` that traverses the chunks of the underlying data. + * + * Example + * @code + * { + * PARCBuffer *dataToChunk = ... + * PARCBufferChunker *chunker = parcBufferChunker_Create(dataToChunk, 32); + * + * PARCIterator *itr = parcBufferChunker_ReverseIterator(chunker); + * + * // use the iterator to traverse the chunker + * } + * @endcode + */ +PARCIterator *parcBufferChunker_ReverseIterator(const PARCBufferChunker *chunker); + +/** + * Get the chunk size of a `PARCBufferChunker.` + * + * @param [in] chunker A `PARCBufferChunker` instance. + * + * @return the chunk size + * + * Example + * @code + * { + * PARCBuffer *dataToChunk = ... + * PARCBufferChunker *chunker = ... + * + * size_t chunkSize = parcBufferChunker_GetChunkSize(chunker); + * } + * @endcode + */ +size_t parcBufferChunker_GetChunkSize(const PARCBufferChunker *chunker); +#endif // libparc_parc_BufferChunker_h diff --git a/libparc/parc/algol/parc_BufferComposer.c b/libparc/parc/algol/parc_BufferComposer.c new file mode 100755 index 00000000..20b7ecee --- /dev/null +++ b/libparc/parc/algol/parc_BufferComposer.c @@ -0,0 +1,289 @@ +/* + * 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 <LongBow/runtime.h> + +#include <stdlib.h> +#include <stdarg.h> +#include <stdio.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_BufferComposer.h> +#include <parc/algol/parc_Buffer.h> + +struct parc_buffer_composer { + size_t incrementHeuristic; + PARCBuffer *buffer; +}; + +static void +_finalize(PARCBufferComposer **bufferPtr) +{ + if (bufferPtr != NULL) { + PARCBufferComposer *composer = *bufferPtr; + parcBufferComposer_OptionalAssertValid(composer); + if (composer->buffer != NULL) { + parcBuffer_Release(&composer->buffer); + } + } +} + +parcObject_ExtendPARCObject(PARCBufferComposer, _finalize, NULL, NULL, parcBufferComposer_Equals, NULL, NULL, NULL); + +static PARCBufferComposer * +_create(void) +{ + return parcObject_CreateInstance(PARCBufferComposer); +} + +/** + * Ensure that the given `PARCBufferComposer` has at least the required number of bytes remaining. + * + * If the remaining capacity (the difference between the capacity of the buffer and its current position) + * of the underlying `PARCBuffer` is less than the required number of bytes, + * the underlying PARCBuffer is expanded with sufficient space to accomodate the required number of bytes. + * + * The position, limit, and mark remain unchanged. + * The capacity is increased. + */ +static PARCBufferComposer * +_ensureRemaining(PARCBufferComposer *composer, size_t required) +{ + parcBufferComposer_OptionalAssertValid(composer); + + size_t remainingCapacity = parcBuffer_Capacity(composer->buffer) - parcBuffer_Position(composer->buffer); + + if (remainingCapacity < required) { + size_t incrementAmount = required; + if (incrementAmount < composer->incrementHeuristic) { + incrementAmount = composer->incrementHeuristic; + } + size_t newCapacity = parcBuffer_Capacity(composer->buffer) + incrementAmount; + + PARCBuffer *newBuffer = parcBuffer_Allocate(newCapacity); + parcBuffer_Flip(composer->buffer); + parcBuffer_PutBuffer(newBuffer, composer->buffer); + parcBuffer_Release(&composer->buffer); + composer->buffer = newBuffer; + } + + return composer; +} + +void +parcBufferComposer_AssertValid(const PARCBufferComposer *composer) +{ + trapIllegalValueIf(composer == NULL, "Parameter must be a non-null pointer to a valid PARCBufferComposer."); + trapIllegalValueIf(composer->incrementHeuristic < sizeof(void *), "Heuristic cannot be < sizeof(void *) (%zd), actual %zd", sizeof(void *), composer->incrementHeuristic); +} + +PARCBufferComposer * +parcBufferComposer_Create(void) +{ + return parcBufferComposer_Allocate(parcMemory_RoundUpToCacheLine(LEVEL1_DCACHE_LINESIZE)); +} + +PARCBufferComposer * +parcBufferComposer_Allocate(size_t size) +{ + PARCBufferComposer *result = _create(); + if (result != NULL) { + result->buffer = parcBuffer_Allocate(size); + result->incrementHeuristic = parcMemory_RoundUpToCacheLine(size); + if (result->buffer == NULL) { + result->incrementHeuristic = sizeof(void *); // minimum size + parcBufferComposer_Release(&result); + } + } + return result; +} + +parcObject_ImplementAcquire(parcBufferComposer, PARCBufferComposer); + +parcObject_ImplementRelease(parcBufferComposer, PARCBufferComposer); + +bool +parcBufferComposer_Equals(const PARCBufferComposer *x, const PARCBufferComposer *y) +{ + if (x == y) { + return true; + } + if (x == NULL || y == NULL) { + return false; + } + + if (x->incrementHeuristic == y->incrementHeuristic) { + if (parcBuffer_Equals(x->buffer, y->buffer)) { + return true; + } + } + + return false; +} + +PARCBufferComposer * +parcBufferComposer_PutArray(PARCBufferComposer *composer, const unsigned char *bytes, size_t length) +{ + if (length > 0) { + composer = _ensureRemaining(composer, length); + if (composer != NULL) { + parcBuffer_PutArray(composer->buffer, length, bytes); + } + } + + return composer; +} + +PARCBufferComposer * +parcBufferComposer_PutChar(PARCBufferComposer *composer, char val) +{ + return parcBufferComposer_PutUint8(composer, (uint8_t) val); +} + +PARCBufferComposer * +parcBufferComposer_PutUint8(PARCBufferComposer *composer, uint8_t byte) +{ + composer = _ensureRemaining(composer, 1); + if (composer != NULL) { + parcBuffer_PutUint8(composer->buffer, byte); + } + + return composer; +} + +PARCBufferComposer * +parcBufferComposer_PutUint16(PARCBufferComposer *composer, uint16_t value) +{ + composer = _ensureRemaining(composer, 2); + if (composer != NULL) { + parcBuffer_PutUint16(composer->buffer, value); + } + + return composer; +} + +PARCBufferComposer * +parcBufferComposer_PutUint32(PARCBufferComposer *composer, uint32_t value) +{ + composer = _ensureRemaining(composer, 4); + if (composer != NULL) { + parcBuffer_PutUint32(composer->buffer, value); + } + + return composer; +} + +PARCBufferComposer * +parcBufferComposer_PutUint64(PARCBufferComposer *composer, uint64_t value) +{ + composer = _ensureRemaining(composer, 8); + if (composer != NULL) { + parcBuffer_PutUint64(composer->buffer, value); + } + + return composer; +} + +PARCBufferComposer * +parcBufferComposer_PutBuffer(PARCBufferComposer *composer, const PARCBuffer *source) +{ + composer = _ensureRemaining(composer, parcBuffer_Remaining(source)); + if (composer != NULL) { + parcBuffer_PutBuffer(composer->buffer, source); + } + + return composer; +} + +PARCBufferComposer * +parcBufferComposer_PutString(PARCBufferComposer *composer, const char *string) +{ + PARCBuffer *wrapper = parcBuffer_AllocateCString(string); + + composer = _ensureRemaining(composer, parcBuffer_Remaining(wrapper)); + if (composer != NULL) { + parcBuffer_PutBuffer(composer->buffer, wrapper); + } + + parcBuffer_Release(&wrapper); + + return composer; +} + +PARCBufferComposer * +parcBufferComposer_PutStrings(PARCBufferComposer *composer, ...) +{ + va_list ap; + va_start(ap, composer); + + char *arg; + while ((arg = va_arg(ap, char *)) != NULL) { + parcBufferComposer_PutString(composer, arg); + } + va_end(ap); + + return composer; +} + +PARCBufferComposer * +parcBufferComposer_Format(PARCBufferComposer *composer, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + char *cString; + ssize_t written = vasprintf(&cString, format, ap); + assertTrue(written >= 0, "Got error from vasprintf"); + va_end(ap); + + parcBufferComposer_PutString(composer, cString); + free(cString); + + return composer; +} + +PARCBuffer * +parcBufferComposer_GetBuffer(const PARCBufferComposer *composer) +{ + return composer->buffer; +} + +PARCBuffer * +parcBufferComposer_CreateBuffer(PARCBufferComposer *composer) +{ + return parcBuffer_Duplicate(composer->buffer); +} + +PARCBuffer * +parcBufferComposer_ProduceBuffer(PARCBufferComposer *composer) +{ + return parcBuffer_Acquire(parcBuffer_Flip(composer->buffer)); +} + +char * +parcBufferComposer_ToString(PARCBufferComposer *composer) +{ + PARCBuffer *buffer = parcBuffer_Flip(parcBuffer_Duplicate(composer->buffer)); + + char *result = parcBuffer_ToString(buffer); + parcBuffer_Release(&buffer); + + return result; +} diff --git a/libparc/parc/algol/parc_BufferComposer.h b/libparc/parc/algol/parc_BufferComposer.h new file mode 100755 index 00000000..f45aeb8e --- /dev/null +++ b/libparc/parc/algol/parc_BufferComposer.h @@ -0,0 +1,571 @@ +/* + * 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. + */ + +/** + * @file parc_BufferComposer.h + * @ingroup memory + * @brief An elastic memory composer of PARCBuffer instances. + * + * A `PARCBufferComposer` is a dynamically allocated buffer that can be used to incrementally add (append) + * intrinsic values and/or `PARCBuffer` instance contents to a single location. It is meant to be a general + * purpose buffer in that all native types may be added to the buffer. When finished, the user can finalize + * the composer and produce a flipped `PARCBuffer` instance. + * + */ +#ifndef libparc_parc_BufferComposer_h +#define libparc_parc_BufferComposer_h + +struct parc_buffer_composer; +typedef struct parc_buffer_composer PARCBufferComposer; + +#include <parc/algol/parc_Buffer.h> + +extern parcObjectDescriptor_Declaration(PARCBufferComposer); + +#ifdef PARCLibrary_DISABLE_VALIDATION +# define parcBufferComposer_OptionalAssertValid(_instance_) +#else +# define parcBufferComposer_OptionalAssertValid(_instance_) parcBufferComposer_AssertValid(_instance_) +#endif + +/** + * Create an empty (zero-length) `PARCBufferComposer`. + * + * @return NULL Memory could not be allocated. + * @return non-NULL A pointer to the new `PARCBufferComposer`. + * + * Example: + * @code + * { + * PARCBufferComposer *composer = parcBufferComposer_Create(); + * + * // insert contents... + * + * parcBufferComposer_Release(&composer); + * } + * @endcode + */ +PARCBufferComposer *parcBufferComposer_Create(void); + +/** + * Create a new instance of `PARCBufferComposer` starting with an initial amount of dynamically allocated memory. + * + * The new buffer's position will be zero, its limit will be set to `length`, its capacity will be set to limit, + * its mark will be undefined, and each of its elements will be initialized to zero. + * + * @param [in] length The number of bytes to pre-allocate. + * + * @return A pointer to a `PARCBufferComposer` instance + * + * Example: + * @code + * { + * PARCBufferComposer *buffer = parcBufferComposer_Allocate(10); + * + * parcBufferComposer_Release(&buffer); + * } + * @endcode + */ +PARCBufferComposer *parcBufferComposer_Allocate(size_t length); + +/** + * Assert that an instance of `PARCBufferComposer` is valid. + * + * If the instance is not valid, terminate via `trapIllegalValue()`. + * + * Valid means the internal state of the type is consistent with its required current or future behaviour. + * This may include the validation of internal instances of types. + * + * @param [in] instance A pointer to a PARCBufferComposer instance. + */ +void parcBufferComposer_AssertValid(const PARCBufferComposer *instance); + +/** + * This function returns a pointer to a shared `PARCBufferComposer`. + * An implementation may choose to produce a whole copy of the original `PARCBufferComposer`, + * or a reference counted pointer to a common copy. + * + * {@link parcBufferComposer_Release()} must perform the right operations to take care of a shared `PARCBufferComposer`, + * or simple copies. + * + * @param [in] original A pointer to a `PARCBufferComposer` that will be copied. + * + * @return A pointer to a `PARCBufferComposer` + * + * Example: + * @code + * { + * PARCBufferComposer *buffer = parcBufferComposer_Allocate(10); + * + * PARCBufferComposer *handle = parcBufferComposer_Acquire(buffer); + * // handle and buffer will be equal + * + * parcBufferComposer_Release(&handle); + * parcBufferComposer_Release(&buffer); + * } + * @endcode + */ +PARCBufferComposer *parcBufferComposer_Acquire(const PARCBufferComposer *original); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] composerPtr A pointer to a pointer to the instance to release. + * + * Example: + * @code + * { + * PARCBufferComposer *composer = parcBufferComposer_Create(); + * + * parcBufferComposer_Release(&composer); + * } + * @endcode + */ +void parcBufferComposer_Release(PARCBufferComposer **composerPtr); + +/** + * Determine if two `PARCBufferComposer` instances are equal. + * + * The following equivalence relations on non-null `PARCBufferComposer` instances are maintained: * + * * It is reflexive: for any non-null reference value x, `parcBufferComposer_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `parcBufferComposer_Equals(x, y)` must return true if and only if + * `parcBufferComposer_Equals(y x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcBufferComposer_Equals(x, y)` returns true and + * `parcBufferComposer_Equals(y, z)` returns true, + * then `parcBufferComposer_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `parcBufferComposer_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `parcBufferComposer_Equals(x, NULL)` must return false. + * + * + * @param [in] x A pointer to a `PARCBufferComposer` instance. + * @param [in] y A pointer to a `PARCBufferComposer` instance. + * + * @return true `PARCBufferComposer` x and y are equal. + * @return false `PARCBufferComposer` x and y are not equal. + * + * Example: + * @code + * { + * PARCBufferComposer *composerA = parcBuffer_Allocate(10); + * PARCBufferComposer *composerB = parcBuffer_Allocate(10); + * + * if (parcBufferComposer_Equals(composerA, composerB)) { + * printf("Composers are equal.\n"); + * } else { + * printf("Composers are NOT equal.\n"); + * } + * + * parcBufferComposer_Release(&composerA); + * parcBufferComposer_Release(&composerB); + * } + * @endcode + */ +bool parcBufferComposer_Equals(const PARCBufferComposer *x, const PARCBufferComposer *y); + +/** + * Append `length` number of bytes from the given byte array `bytes` to the given `PARCBufferComposer`. + * + * The input `PARCBufferComposer` instance is modified. + * + * @param [in,out] composer A pointer to the PARCBufferComposer to receive the data. + * @param [in] bytes A pointer to the array that contains the data. + * @param [in] length The number of bytes of data to put into @p buffer. + * + * @return NULL Memory could not be allocated, and the buffer is unmodified. + * @return non-NULL The value of the parameter @p buffer. + * + * Example: + * @code + * PARCBufferComposer *composer = parcBufferComposer_Allocate(10); + * uint8_t string[6] = {'H', 'e', 'l', 'l', 'o', '\0'}; + * + * parcBufferComposer_PutArray(composer, string, strlen(string)); + * + * parcBufferComposer_Release(&composer); + * @endcode + */ +PARCBufferComposer *parcBufferComposer_PutArray(PARCBufferComposer *composer, const unsigned char *bytes, size_t length); + +/** + * Append a single char to the given `PARCBufferComposer` at the current position. + * + * The buffer's position will be advanced by 1 (== sizeof(char)). + * The input `PARCBufferComposer` instance is modified. + * + * @param [in,out] composer A pointer to a `PARCBufferComposer` instance that will receive the char value. + * @param [in] value A single char value. + * + * @return NULL Memory could not be allocated. + * @return non-NULL The value of the parameter @p buffer. + * + * Example: + * @code + * { + * PARCBufferComposer *composer = parcBufferComposer_Allocate(10); + * parcBufferComposer_PutChar(composer, 0x12); + * } + * @endcode + */ +PARCBufferComposer *parcBufferComposer_PutChar(PARCBufferComposer *composer, char value); + +/** + * Append a single uint8_t to the given `PARCBufferComposer` at the current position. + * + * The buffer's position will be advanced by 1 (== sizeof(uint8_t)). + * The input `PARCBufferComposer` instance is modified. + * + * @param [in,out] composer A pointer to a `PARCBufferComposer` instance that will receive the uint8_t value. + * @param [in] value A uint8_t value. + * + * @return NULL Memory could not be allocated. + * @return non-NULL The value of the parameter @p buffer. + * + * Example: + * @code + * { + * PARCBufferComposer *composer = parcBufferComposer_Allocate(10); + * parcBufferComposer_PutUint8(composer, 0x12); + * } + * @endcode + */ +PARCBufferComposer *parcBufferComposer_PutUint8(PARCBufferComposer *composer, uint8_t value); + +/** + * Append a single uint16_t to the given `PARCBufferComposer` at the current position. + * + * The buffer's position will be advanced by 2. + * The input `PARCBufferComposer` instance is modified. + * + * @param [in,out] composer A pointer to a `PARCBufferComposer` instance that will receive the uint16_t value. + * @param [in] value A uint16_t value. + * + * @return NULL Memory could not be allocated. + * @return non-NULL The value of the parameter @p buffer. + * + * Example: + * @code + * { + * PARCBufferComposer *composer = parcBufferComposer_Allocate(10); + * parcBufferComposer_PutUint16(composer, 0x1234); + * } + * @endcode + */ +PARCBufferComposer *parcBufferComposer_PutUint16(PARCBufferComposer *composer, uint16_t value); + +/** + * Append a single uint16_t to the given `PARCBufferComposer` at the current position. + * + * The buffer's position will be advanced by 4. + * The input `PARCBufferComposer` instance is modified. + * + * @param [in,out] composer A pointer to a `PARCBufferComposer` instance that will receive the uint32_t value. + * @param [in] value A `uint32_t` value. + * + * @return NULL Memory could not be allocated. + * @return non-NULL The value of the parameter @p composer. + * + * Example: + * @code + * { + * PARCBufferComposer *composer = parcBufferComposer_Allocate(10); + * parcBufferComposer_PutUint32(composer, 0x12345678); + * } + * @endcode + */ +PARCBufferComposer *parcBufferComposer_PutUint32(PARCBufferComposer *composer, uint32_t value); + +/** + * Append a single uint64_t to the given `PARCBufferComposer` at the current position. + * + * The value is encoded in full, 8 byte, form in big-endian format, or network byte order. + * The buffer's position will be advanced by 8. + * The input `PARCBufferComposer` instance is modified. + * + * @param [in,out] composer A pointer to a `PARCBufferComposer` instance that will receive the uint64_t value. + * @param [in] value A `uint64_t` value. + * + * @return NULL Memory could not be allocated. + * @return non-NULL The value of the parameter @p composer. + * + * Example: + * @code + * { + * PARCBufferComposer *composer = parcBufferComposer_Allocate(20); + * parcBufferComposer_PutUint32(composer, 0x0123456789ABCDEF); + * } + * @endcode + */ +PARCBufferComposer *parcBufferComposer_PutUint64(PARCBufferComposer *composer, uint64_t value); + +/** + * Put (append) the content of the source buffer into the destination buffer. + * + * The contents are taken from the current position of the source buffer + * to its limit. The destination buffer is expanded as necessary. + * + * When complete, the source buffer's position is set to its limit. + * + * Both the input `PARCBufferComposer` and `PARCBuffer` instances are modified. + * + * @param [in,out] composer A pointer to a `PARCBufferComposer` instance. + * @param [in] sourceBuffer The buffer that will produce the data. + * + * @return NULL Memory could not be allocated. + * @return non-NULL The value of the parameter @p composer. + * + * Example: + * @code + * { + * PARCBufferComposer *composer = parcBufferComposer_Allocate(1024); + * PARCBuffer *buffer = parcBuffer_AllocateCString("Hello, World!"); + * parcBufferComposer_PutBuffer(composer, buffer); + * } + * @endcode + * + * @see parcBufferComposer_GetBuffer + */ +PARCBufferComposer *parcBufferComposer_PutBuffer(PARCBufferComposer *composer, const PARCBuffer *sourceBuffer); + +/** + * Put (append) the content of the null-terminated, C-style string into the destination buffer. + * + * The input `PARCBufferComposer` instance is modified. + * + * @param [in,out] composer A pointer to a `PARCBufferComposer` instance. + * @param [in] cString A pointer to a null-terminated C string to append to this `PARCBufferComposer`. + * + * @return NULL Memory could not be allocated. + * @return non-NULL The value of the parameter @p composer. + * + * Example: + * @code + * { + * PARCBufferComposer *composer = parcBufferComposer_Allocate(1024); + * parcBufferComposer_PutString(composer, "Hello, World!"); + * // Hello, World! + * } + * @endcode + * + * @see parcBufferComposer_PutBuffer + * @see parcBufferComposer_GetBuffer + */ +PARCBufferComposer *parcBufferComposer_PutString(PARCBufferComposer *composer, const char *cString); + +/** + * Put (append) the content of an arbitrary number of null-terminated, C-style strings + * into the destination buffer. + * + * The input `PARCBufferComposer` instance is modified. + * + * @param [in,out] composer A pointer to a `PARCBufferComposer` instance. + * @param [in] ... The null-terminated, C-style strings to append to the given `PARCBufferComposer`. + * + * @return NULL Memory could not be allocated. + * @return non-NULL The value of the parameter @p buffer. + * + * Example: + * @code + * { + * PARCBufferComposer *composer = parcBufferComposer_Allocate(1024); + * parcBufferComposer_PutString(composer, "Hello"); + * parcBufferComposer_PutStrings(composer, ", ", "World", "!"); + * // Hello, World! + * } + * @endcode + * + * @see parcBufferComposer_PutString + * @see parcBufferComposer_PutBuffer + * @see parcBufferComposer_GetBuffer + */ +PARCBufferComposer *parcBufferComposer_PutStrings(PARCBufferComposer *composer, ...); + +/** + * Append a formatted nul-terminated, C string string to the given `PARCBufferComposer` instance. + * The input `PARCBufferComposer` instance is modified. + * + * The format string is a nul-terminated C string compatible with the `vasprintf(3)` C library function. + * + * @param [in,out] composer A pointer to `PARCBufferComposer`. + * @param [in] format The format string compatible with the `vasprintf(3)` C library function. + * @param [in] ... Remaining parameters used to format the string. + * + * @return The same pointer as the `composer` parameter. + * + * Example: + * @code + * { + * PARCBufferComposer *composer = parcBufferComposer_Allocate(1024); + * + * parcBufferComposer_Format(composer, "Hello %s\n", "World"); + * + * char *string = parcBuffer_ToString(parcBufferComposer_ProduceBuffer(string))); + * printf("String = %s\n", string); + * + * parcMemory_Deallocate(&string); + * } + * @endcode + */ +PARCBufferComposer *parcBufferComposer_Format(PARCBufferComposer *composer, const char *format, ...) +__attribute__((format(printf, 2, 3))); + +/** + * Return a pointer to the underlying {@link PARCBuffer} instance currently used by the given `PARCBufferComposer`. + * + * WARNING: This function is not safe. Use with caution. + * + * There is no guarantee that the returned `PARCBuffer` instance will not + * be released and deallocated before use by the caller of this function. + * If modifications need to be made, a reference should be acquired manually. + * + * Also, if the caller modifies the state of the returned `PARCBuffer` instance, + * e.g., via a {@link parcBuffer_GetUint8}() call, any future writes to this + * same `PARCBufferComposer` will not behave as expected unless the instance is + * returned to its original state. + * + * To safely access the underlying `PARCBuffer` instance, use + * the {@link parcBufferComposer_CreateBuffer}() function instead. + * + * No new reference is created. The caller must acquire a reference to the returned `PARCBuffer` + * if it needs retain it beyond the life of the given `PARCBufferComposer`. + * + * @param composer [in] A pointer to a `PARCBufferComposer` instance. + * + * @return A pointer to the internal `PARCBuffer` which is wrapped by this `PARCBufferComposer`. + * + * Example: + * @code + * { + * PARCBufferComposer *composer = parcBufferComposer_Allocate(1024); + * PARCBuffer *buffer = parcBuffer_AllocateCString("Hello, World!"); + * parcBufferComposer_PutBuffer(composer, buffer); + * + * PARCBuffer *sameBuffer = parcBufferComposer_GetBuffer(composer); + * // buffer and sameBuffer are equal + * } + * @endcode + * + * @see parcBufferComposer_PutBuffer + * @see parcBufferComposer_ProduceBuffer + */ +PARCBuffer *parcBufferComposer_GetBuffer(const PARCBufferComposer *composer); + +/** + * Create a `PARCBuffer` pointing to the underlying `PARCBuffer` instance. + * + * This is functionally equivalent to {@link parcBufferComposer_GetBuffer} but + * is safe since it allocates a new `PARCBuffer` instance for the same buffer. + * + * The result must be freed by the caller via {@link parcBuffer_Release}. + * + * @param [in] composer A pointer to a `PARCBufferComposer` instance. + * + * @return A pointer to the `PARCBuffer` which is stored by this `PARCBufferComposer`. + * + * Example: + * @code + * { + * PARCBufferComposer *composer = parcBufferComposer_Allocate(1024); + * PARCBuffer *buffer = parcBuffer_AllocateCString("Hello, World!"); + * parcBufferComposer_PutBuffer(composer, buffer); + * + * PARCBuffer *writeBuffer = parcBufferComposer_CreateBuffer(composer); + * + * // use writeBuffer as needed + * + * parcBuffer_Release(&writeBuffer); + * } + * @endcode + * + * @see parcBufferComposer_GetBuffer + * @see parcBufferComposer_ProduceBuffer + */ +PARCBuffer *parcBufferComposer_CreateBuffer(PARCBufferComposer *composer); + +/** + * Finalize this `PARCBufferComposer` and return the resulting {@link PARCBuffer}. + * + * Note that, unlike {@link parcBufferComposer_GetBuffer}, the return buffer is flipped + * via {@link parcBuffer_Flip}. This effectively finalizes this `PARCBufferComposer`. + * No more writes should be made to this instance. + * + * Also note that {@link parcBufferComposer_ToString} cannot be called after this function + * is invoked. + * + * The result must be freed by the caller via {@link parcBuffer_Release}. + * + * @param [in] composer A pointer to a `PARCBufferComposer` instance. + * + * @return A pointer to the final `PARCBuffer` which is stored by this `PARCBufferComposer` + * + * Example: + * @code + * { + * PARCBufferComposer *composer = parcBufferComposer_Allocate(1024); + * PARCBuffer *buffer = parcBuffer_AllocateCString("Hello, World!"); + * parcBufferComposer_PutBuffer(composer, buffer); + * + * PARCBuffer *readBuffer = parcBufferComposer_ProduceBuffer(composer); + * parcBufferComposer_Release(&composer); + * + * // use readBuffer as needed + * + * parcBuffer_Release(&readBuffer); + * } + * @endcode + * + * @see parcBufferComposer_GetBuffer + */ +PARCBuffer *parcBufferComposer_ProduceBuffer(PARCBufferComposer *composer); + +/** + * Produce a null-terminated string containing the characters from 0 to the current + * position of the given `PARCBufferComposer`. + * The composer is not modified and may continue to be used. + * + * The result must be freed by the caller via {@link parcMemory_Deallocate}. + * + * @param [in] instance A pointer to a valid PARCBufferComposer instance. + * + * @return NULL Cannot allocate memory. + * @return non-NULL A pointer to an allocated, null-terminated C string that must be deallocated via {@link parcMemory_Deallocate}. + * + * Example: + * @code + * { + * PARCBufferComposer *a = parcBufferComposer_Create(); + * + * char *string = parcBufferComposer_ToString(a); + * + * parcBufferComposer_Release(&a); + * + * parcMemory_Deallocate(&string); + * } + * @endcode + */ +char *parcBufferComposer_ToString(PARCBufferComposer *composer); +#endif // libparc_parc_BufferComposer_h diff --git a/libparc/parc/algol/parc_BufferDictionary.c b/libparc/parc/algol/parc_BufferDictionary.c new file mode 100755 index 00000000..56ed800a --- /dev/null +++ b/libparc/parc/algol/parc_BufferDictionary.c @@ -0,0 +1,146 @@ +/* + * 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. + */ + +/** + * The dictionary is implemented with the parc_HashCodeTable backend. This implementation + * is inefficient for additions with duplicate keys, because the semantics of parc_HashCodeTable + * are not the same as parc_BufferDictionary in returning values for Put and Remove. + * + */ +#include <config.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_BufferDictionary.h> + +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_HashCodeTable.h> + +struct parc_buffer_dictionary { + PARCHashCodeTable *hashtable; +}; + +// Wrapper to go from void * to PARCBuffer * +static bool +_parcBufferEquals(const void *a, const void *b) +{ + return parcBuffer_Equals((const PARCBuffer *) a, (const PARCBuffer *) b); +} + +// Wrapper to go from void * to PARCBuffer * +static PARCHashCode +_parcBufferHashCode(const void *a) +{ + return parcBuffer_HashCode((const PARCBuffer *) a); +} + +// Wrapper to go from void ** to PARCBuffer ** +static void +_parcBufferRelease(void **bufferVoidPtr) +{ + parcBuffer_Release((PARCBuffer **) bufferVoidPtr); +} + +/* + * Initialise a parcBufferDictionary instance. + * @return The same pointer as `result`. + */ +static PARCBufferDictionary * +_init(PARCBufferDictionary *result) +{ + result->hashtable = parcHashCodeTable_Create(_parcBufferEquals, _parcBufferHashCode, _parcBufferRelease, _parcBufferRelease); + return result; +} + +/** + * Cleans up the internal memory of a PARCBufferDictionary + * + * @param [in] dictionaryPtr Double pointer to the dictionary to finalize + */ +static void +_destroy(PARCBufferDictionary **dictionaryPtr) +{ + assertNotNull(dictionaryPtr, "Double pointer dictionaryPtr must be non-NULL"); + assertNotNull(*dictionaryPtr, "Double pointer dictionaryPtr must dereference to non-NULL"); + + PARCBufferDictionary *dict = *dictionaryPtr; + + parcHashCodeTable_Destroy(&dict->hashtable); +} + + +parcObject_ExtendPARCObject(PARCBufferDictionary, _destroy, NULL, NULL, NULL, NULL, NULL, NULL); + +PARCBufferDictionary * +parcBufferDictionary_Create(void) +{ + PARCBufferDictionary *result = parcObject_CreateInstance(PARCBufferDictionary); + if (result != NULL) { + return _init(result); + } + + return NULL; +} + +parcObject_ImplementAcquire(parcBufferDictionary, PARCBufferDictionary); + +parcObject_ImplementRelease(parcBufferDictionary, PARCBufferDictionary); + +PARCBuffer * +parcBufferDictionary_Put(PARCBufferDictionary *dictionary, PARCBuffer *key, PARCBuffer *value) +{ + assertNotNull(dictionary, "Parameter dictionary must be non-NULL"); + assertNotNull(key, "Parameter key must be non-NULL"); + assertNotNull(value, "Parameter value must be non-NULL"); + + PARCBuffer *oldValue = NULL; + + // We use reference counted copyies of the supplied parameters + PARCBuffer *key_copy = parcBuffer_Acquire(key); + PARCBuffer *value_copy = parcBuffer_Acquire(value); + + if (!parcHashCodeTable_Add(dictionary->hashtable, key_copy, value_copy)) { + // parcHashCodeTable_Del will free the referece, to make a copy of it + PARCBuffer *original = (PARCBuffer *) parcHashCodeTable_Get(dictionary->hashtable, key_copy); + oldValue = parcBuffer_Acquire(original); + parcHashCodeTable_Del(dictionary->hashtable, key_copy); + parcHashCodeTable_Add(dictionary->hashtable, key_copy, value_copy); + } + + return oldValue; +} + +PARCBuffer * +parcBufferDictionary_Get(PARCBufferDictionary *dictionary, PARCBuffer *key) +{ + assertNotNull(dictionary, "Parameter dictionary must be non-NULL"); + assertNotNull(key, "Parameter key must be non-NULL"); + + return parcHashCodeTable_Get(dictionary->hashtable, key); +} + +PARCBuffer * +parcBufferDictionary_Remove(PARCBufferDictionary *dictionary, PARCBuffer *key) +{ + assertNotNull(dictionary, "Parameter dictionary must be non-NULL"); + assertNotNull(key, "Parameter key must be non-NULL"); + + // parcHashCodeTable_Del will free the referece, to make a copy of it + PARCBuffer *original = (PARCBuffer *) parcHashCodeTable_Get(dictionary->hashtable, key); + PARCBuffer *oldValue = parcBuffer_Acquire(original); + parcHashCodeTable_Del(dictionary->hashtable, key); + return oldValue; +} diff --git a/libparc/parc/algol/parc_BufferDictionary.h b/libparc/parc/algol/parc_BufferDictionary.h new file mode 100755 index 00000000..60aa90cb --- /dev/null +++ b/libparc/parc/algol/parc_BufferDictionary.h @@ -0,0 +1,145 @@ +/* + * 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. + */ + +/** + * @file parc_BufferDictionary.h + * @ingroup datastructures + * @brief A key/value dictionary built around PARCBuffer as the Key and the Value. + * + * The key/value dictionary models the Java MAP interface. It is built around Put, Get, and Remove. + * The dictionary stores references to the Key and Value, so the caller may destroy its references + * if no longer needed. + * + */ +#ifndef libparc_parc_BufferDictionary_h +#define libparc_parc_BufferDictionary_h + +typedef struct parc_buffer_dictionary PARCBufferDictionary; + +#include <parc/algol/parc_Buffer.h> + + +/** + * Creates an empty dictionary. You must use {@link parcBufferDictionary_Release} to destroy it. + * + * @return A pointer to the new `PARCBufferDictionary` + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCBufferDictionary *parcBufferDictionary_Create(void); + +/** + * Increase the number of references to a `PARCBufferDictionary`. + * + * Note that a new `PARCBufferDictionary` is not created, + * only that the given `PARCBufferDictionary` reference count is incremented. + * Discard the reference by invoking {@link parcBufferDictionary_Release}. + * + * @param [in] dictionary is a pointer to a `PARCBufferDictionary` instance + * @return The pointer to the @p dictionary instance. + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCBufferDictionary *parcBufferDictionary_Acquire(const PARCBufferDictionary *dictionary); + +/** + * Release a `PARCBufferDictionary` reference. + * + * Only the last invocation where the reference count is decremented to zero, + * will actually destroy the `PARCBufferDictionary`. + * + * @param [in,out] dictionaryPtr is a pointer to the `PARCBufferDictionary` reference. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void parcBufferDictionary_Release(PARCBufferDictionary **dictionaryPtr); + +/** + * Add a key/value to the dictionary, returns previous value or NULL + * + * The Dictionary will store a reference (PARCBuffer::aquire) to the key and to the value. + * The Key and Value must be non-NULL. If a previous entry for the key is in the dictionary, + * the previous value is returned. THE CALLER MUST USE {@link parcBuffer_Release} on the returned + * value if it is non-NULL; + * + * @param [in,out] dictionary The dictionary to modify + * @param [in] key The dictionary key + * @param [in] value The value for the key + * + * @return NULL The inserted key is unique + * @return non-NULL Returns the previous value of the key, must use {@link parcBuffer_Release} + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCBuffer *parcBufferDictionary_Put(PARCBufferDictionary *dictionary, PARCBuffer *key, PARCBuffer *value); + +/** + * Returns the value associated with the key, or NULL if does not exist + * + * The returned value is what's stored in the dictionary in a `PARCBuffer` instance. If the user wishes to keep the + * value beyond the current calling scope, she should use {@link parcBuffer_Acquire} on the + * returned value. + * + * @param [in] dictionary The dictionary to query + * @param [in] key The dictionary key + * + * @return NULL The key is not in the dictionary + * @return non-NULL Returns the current value of the key, DO NOT use {@link parcBuffer_Release} + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCBuffer *parcBufferDictionary_Get(PARCBufferDictionary *dictionary, PARCBuffer *key); + +/** + * Removes a key from the dictionary, returning the current value or NULL. The caller MUST + * call {@link parcBuffer_Release} on the returned value. + * + * @param [in,out] dictionary The dictionary to modify + * @param [in] key The dictionary key + * + * @return NULL The inserted key is not in the dictionary + * @return non-NULL Returns the current value of the key, DO NOT use {@link parcBuffer_Release} + * + * Example: + * @code + * { + * uint8_t phone[] = "6505551212"; + * PARCBufferDictionary *dict = parcBufferDictionary_Create(); + * + * parcBuffer *key = parcBuffer_Wrap(phone, sizeof(phone), 0); + * parcBuffer_Release(parcBufferDictionary_Remove(dict, key)); + * + * parcBuffer_Release(&key); + * parcBufferDictionary_Destroy(&dict); + * } + * @endcode + */ +PARCBuffer *parcBufferDictionary_Remove(PARCBufferDictionary *dictionary, PARCBuffer *key); +#endif // libparc_parc_BufferDictionary_h diff --git a/libparc/parc/algol/parc_ByteArray.c b/libparc/parc/algol/parc_ByteArray.c new file mode 100644 index 00000000..762421bd --- /dev/null +++ b/libparc/parc/algol/parc_ByteArray.c @@ -0,0 +1,312 @@ +/* + * 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 <stdio.h> +#include <ctype.h> +#include <string.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_ByteArray.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_Hash.h> +#include <parc/algol/parc_DisplayIndented.h> + +struct parc_byte_array { + uint8_t *array; + size_t length; + void (*freeFunction)(void **); +}; +#define MAGIC 0x0ddba11c1a55e5 + +static inline void +_trapIfOutOfBounds(const PARCByteArray *array, const size_t index) +{ + trapOutOfBoundsIf(index >= array->length, "parcByteArray index %zd exceeds the length %zd", index, array->length); +} + +static bool +_parcByteArray_Destructor(PARCByteArray **byteArrayPtr) +{ + PARCByteArray *byteArray = *byteArrayPtr; + + if (byteArray->freeFunction != NULL) { + if (byteArray->array != NULL) { + byteArray->freeFunction((void **) &(byteArray->array)); + } + } + return true; +} + +parcObject_Override(PARCByteArray, PARCObject, + .destructor = (PARCObjectDestructor *) _parcByteArray_Destructor, + .copy = (PARCObjectCopy *) parcByteArray_Copy, + .equals = (PARCObjectEquals *) parcByteArray_Equals, + .compare = (PARCObjectCompare *) parcByteArray_Compare, + .hashCode = (PARCObjectHashCode *) parcByteArray_HashCode, + .display = (PARCObjectDisplay *) parcByteArray_Display); + +void +parcByteArray_AssertValid(const PARCByteArray *instance) +{ + trapInvalidValueIf(parcByteArray_IsValid(instance) == false, + "PARCByteArray instance is invalid."); +} + +bool +parcByteArray_IsValid(const PARCByteArray *instance) +{ + bool result = false; + + if (instance != NULL) { + if (instance->length == 0 || instance->array != NULL) { + result = true; + } + } + + return result; +} + +PARCByteArray * +parcByteArray_Allocate(const size_t length) +{ + uint8_t *array = NULL; + if (length > 0) { + array = parcMemory_AllocateAndClear(sizeof(uint8_t) * length); + if (array == NULL) { + return NULL; + } + } + PARCByteArray *result = parcObject_CreateInstance(PARCByteArray); + + if (result != NULL) { + result->array = array; + result->length = length; + result->freeFunction = parcMemory_DeallocateImpl; + return result; + } else { + parcMemory_Deallocate(&array); + } + return NULL; +} + +PARCByteArray * +parcByteArray_Wrap(const size_t length, uint8_t array[length]) +{ + if (array != NULL) { + PARCByteArray *result = parcObject_CreateInstance(PARCByteArray); + if (result != NULL) { + result->array = array; + result->length = length; + result->freeFunction = NULL; + return result; + } + } + return NULL; +} + +parcObject_ImplementAcquire(parcByteArray, PARCByteArray); + +parcObject_ImplementRelease(parcByteArray, PARCByteArray); + +PARCByteArray * +parcByteArray_Copy(const PARCByteArray *original) +{ + parcByteArray_OptionalAssertValid(original); + + PARCByteArray *result = NULL; + + if (original != NULL) { + result = parcByteArray_Allocate(original->length); + memcpy(result->array, original->array, result->length); + } + + return result; +} + +size_t +parcByteArray_Capacity(const PARCByteArray *byteArray) +{ + parcByteArray_OptionalAssertValid(byteArray); + return byteArray->length; +} + +PARCByteArray * +parcByteArray_PutByte(PARCByteArray *result, size_t index, uint8_t byte) +{ + parcByteArray_OptionalAssertValid(result); + _trapIfOutOfBounds(result, index); + + result->array[index] = byte; + return result; +} + +uint8_t +parcByteArray_GetByte(const PARCByteArray *array, size_t index) +{ + parcByteArray_OptionalAssertValid(array); + _trapIfOutOfBounds(array, index); + + return array->array[index]; +} + +uint8_t * +parcByteArray_Array(const PARCByteArray *byteArray) +{ + parcByteArray_OptionalAssertValid(byteArray); + return byteArray->array; +} + +uint8_t * +parcByteArray_AddressOfIndex(const PARCByteArray *array, const size_t index) +{ + parcByteArray_OptionalAssertValid(array); + _trapIfOutOfBounds(array, index); + + return &array->array[index]; +} + +int +parcByteArray_Compare(const PARCByteArray *x, const PARCByteArray *y) +{ + if (x == y) { + return 0; + } + + if (x == NULL) { + return -1; + } + + if (y == NULL) { + return +1; + } + + if (parcByteArray_Capacity(x) > parcByteArray_Capacity(y)) { + return +1; + } + if (parcByteArray_Capacity(x) < parcByteArray_Capacity(y)) { + return -1; + } + + return memcmp(x->array, y->array, parcByteArray_Capacity(x)); +} + +PARCByteArray * +parcByteArray_PutBytes(PARCByteArray *result, size_t offset, size_t length, const uint8_t source[length]) +{ + parcByteArray_OptionalAssertValid(result); + trapOutOfBoundsIf(offset > result->length, + "The offset (%zd) exeeds the length (%zd) of the PARCByteArray.", offset, result->length); + + size_t available = result->length - offset; + + trapOutOfBoundsIf(length > available, "%zd available bytes, %zd required.", available, length); + + memcpy(&result->array[offset], source, length); + return result; +} + +PARCByteArray * +parcByteArray_GetBytes(const PARCByteArray *result, size_t offset, size_t length, uint8_t array[length]) +{ + parcByteArray_OptionalAssertValid(result); + + size_t available = result->length - offset; + + trapOutOfBoundsIf(length > available, "parcByteArray_CopyOut %zd available bytes, %zd required", available, length); + + memcpy(array, &result->array[offset], length); + return (PARCByteArray *) result; +} + +PARCByteArray * +parcByteArray_ArrayCopy(PARCByteArray *destination, size_t destOffset, const PARCByteArray *source, size_t srcOffset, size_t length) +{ + parcByteArray_OptionalAssertValid(destination); + parcByteArray_OptionalAssertValid(source); + + memcpy(&destination->array[destOffset], &source->array[srcOffset], length); + return destination; +} + +bool +parcByteArray_Equals(const PARCByteArray *a, const PARCByteArray *b) +{ + if (a == b) { + return true; + } + if (a == NULL || b == NULL) { + return false; + } + if (a->length == b->length) { + return memcmp(a->array, b->array, a->length) == 0; + } + return false; +} + +static void +_parcByteArray_PrettyPrintLine(const unsigned char *memory, size_t offset, size_t length) +{ + int bytesPerLine = 16; + char accumulator[bytesPerLine + 1]; + memset(accumulator, ' ', bytesPerLine); + accumulator[bytesPerLine] = 0; + + printf("%5zd: ", offset); + + for (int i = 0; i < bytesPerLine; i++) { + if (offset + i >= length) { + printf(" "); + accumulator[i] = ' '; + } else { + char c = memory[offset + i]; + printf("0x%02x, ", c & 0xFF); + if (isprint(c)) { + accumulator[i] = c; + } else { + accumulator[i] = '.'; + } + } + } + printf(" %s\n", accumulator); +} + +void +parcByteArray_Display(const PARCByteArray *array, int indentation) +{ + int bytesPerLine = 16; + + if (array->array == NULL) { + parcDisplayIndented_PrintLine(indentation, "PARCByteArray@NULL"); + } else { + parcDisplayIndented_PrintLine(indentation, "PARCByteArray@%p = [0,%zd)", (void *) array, array->length); + + for (size_t offset = 0; offset < array->length; offset += bytesPerLine) { + _parcByteArray_PrettyPrintLine(array->array, offset, array->length); + } + } +} + +PARCHashCode +parcByteArray_HashCode(const PARCByteArray *array) +{ + parcByteArray_OptionalAssertValid(array); + return parcHashCode_Hash(array->array, array->length); +} diff --git a/libparc/parc/algol/parc_ByteArray.h b/libparc/parc/algol/parc_ByteArray.h new file mode 100644 index 00000000..98cc970a --- /dev/null +++ b/libparc/parc/algol/parc_ByteArray.h @@ -0,0 +1,580 @@ +/* + * 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. + */ + +/** + * @file parc_ByteArray.h + * @ingroup memory + * @brief A simple reference counted unsigned byte array. + * + * @htmlonly + * <svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" version="1.1" viewBox="48 74 304 68" width="304pt" height="68pt"> + * <defs> + * <font-face font-family="Helvetica Neue" font-size="10" panose-1="2 0 5 3 0 0 0 2 0 4" units-per-em="1000" underline-position="-100" underline-thickness="50" slope="0" x-height="517" cap-height="714" ascent="951.99585" descent="-212.99744" font-weight="500"> + * <font-face-src> + * <font-face-name name="HelveticaNeue"/> + * </font-face-src> + * </font-face> + * <marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="black"> + * <g> + * <path d="M 8 0 L 0 -3 L 0 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/> + * </g> + * </marker> + * </defs> + * <g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"> + * <title>icon_512x512</title> + * <rect fill="white" width="512" height="512"/> + * <g> + * <title>Layer 1</title> + * <text transform="translate(271 88.5)" fill="black"> + * <tspan font-family="Helvetica Neue" font-size="10" font-weight="500" x=".185" y="10" textLength="39.63">Capacity</tspan> + * </text> + * <text transform="translate(83 88.5)" fill="black"> + * <tspan font-family="Helvetica Neue" font-size="10" font-weight="500" x=".245" y="10" textLength="23.51">Array</tspan> + * </text> + * <path d="M 78 96.483333 C 73.6671 96.98884 67.757544 94.49623 65 98 C 63.49089 99.917494 62.925312 103.63153 62.52875 107.66717" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + * <rect x="59" y="118" width="281" height="13" fill="white"/> + * <rect x="59" y="118" width="281" height="13" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + * <path d="M 316 97.11628 C 321.9994 97.744123 330.42197 95.601626 334 99 C 335.8631 100.769544 336.41326 104.04195 336.67595 107.643264" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + * </g> + * </g> + * </svg> + * @endhtmlonly + * + * `PARCByteArray` is a simple reference counted array of `uint8_t` values. + * Instances of `PARCByteArray` are created either by dynamically allocating the byte array, + * via `parcByteArray_Allocate()`, + * or by wrapping a static `uint8_t` array, + * via `parcByteArray_Wrap()`. + * + * New references to an existing instance of `PARCByteArray` are created via `parcByteArray_Acquire()`. + * + * A `PARCByteArray` reference is released via `parcByteArray_Release`. + * Only the last invocation will deallocated the `PARCByteArray`. + * If the `PARCByteArray` references dynamically allocated memory, + * that memory is freed with the `PARCByteArray` when the last reference is released. + * + */ +#ifndef libparc_parc_ByteArray_h +#define libparc_parc_ByteArray_h +#include <stdlib.h> +#include <stdbool.h> +#include <stdint.h> + +struct parc_byte_array; +/** + * + * @see {@link parcByteArray_Allocate} + * @see {@link parcByteArray_Wrap} + */ +typedef struct parc_byte_array PARCByteArray; + +#include <parc/algol/parc_HashCode.h> + +#ifdef PARCLibrary_DISABLE_VALIDATION +# define parcByteArray_OptionalAssertValid(_instance_) +#else +# define parcByteArray_OptionalAssertValid(_instance_) parcByteArray_AssertValid(_instance_) +#endif + +/** + * Assert that an instance of `PARCByteArray` is valid. + * + * If the instance is not valid, terminate via {@link trapIllegalValue} + * + * Valid means the internal state of the type is consistent with its required current or future behaviour. + * This may include the validation of internal instances of types. + * + * @param [in] instance A pointer to a `PARCByteArray` instance. + */ +void parcByteArray_AssertValid(const PARCByteArray *instance); + +/** + * Determine if an instance of `PARCByteArray` is valid. + * + * Valid means the internal state of the type is consistent with its required current or future behaviour. + * This may include the validation of internal instances of types. + * + * @param [in] instance A pointer to a `PARCByteArray` instance. + * + * @return true The instance is valid. + * @return false The instance is not valid. + */ +bool parcByteArray_IsValid(const PARCByteArray *instance); + +/** + * Dynamically allocate a `PARCByteArray` of a specific capacity. + * + * @param [in] capacity The number of bytes in the byte array. + * + * @return A pointer to an allocated `PARCByteArray` instance which must be released via {@link parcByteArray_Release()}. + * + * Example: + * @code + * { + * PARCByteArray *byteArray = parcByteArray_Allocate(100); + * + * parcByteArray_Release(&byteArray); + * } + * @endcode + * + * @see parcByteArray_Release + */ +PARCByteArray *parcByteArray_Allocate(const size_t capacity); + +/** + * Wrap existing memory in a {@link PARCByteArray}. + * + * As in all `Wrap` functions, a copy of the memory is not made. + * Be sure to only wrap memory that is either global, + * or used within on stack-frame or functional block. + * Otherwise, corruption is likly to occur. + * + * @param [in] capacity The maximum capacity of the backing array. + * @param [in] array A pointer to the backing array. + * + * @return A pointer to an allocated `PARCByteArray` instance which must be released via {@link parcByteArray_Release()}. + * + * Example: + * @code + * { + * uint8_t array[10]; + * PARCByteArray *byteArray = parcByteArray_Wrap(array, 10); + * + * parcByteArray_Release(&byteArray); + * } + * @endcode + */ +PARCByteArray *parcByteArray_Wrap(size_t capacity, uint8_t *array); + +/** + * Returns the pointer to the `uint8_t` array that backs this `PARCByteArray`. + * + * Modifications to the given `PARCByteArray` content will cause the returned array's content to be modified, and vice versa. + * + * The reference count for the given `PARCByteArray` is not modified. + * + * <b>Use with caution.</b> + * + * @param [in] byteArray Pointer to a `PARCByteArray` instance from which the underlying array is extracted. + * + * @return The pointer to the `uint8_t` array that backs this `PARCByteArray`. + * + * Example: + * @code + * { + * uint8_t array[10]; + * PARCByteArray *byteArray = parcByteArray_Wrap(array, 10); + * + * uint8_t *raw = parcByteArray_Array(byteArray); + * // updates on the raw array (pointer) are seen in the byteArray instance + * // use with caution + * + * parcByteArray_Release(&byteArray); + * } + * @endcode + */ +uint8_t *parcByteArray_Array(const PARCByteArray *byteArray); + +/** + * Increase the number of references to a `PARCByteArray`. + * + * Note that a new `PARCByteArray` is not created, + * only that the given `PARCByteArray` reference count is incremented. + * Discard the reference by invoking {@link parcByteArray_Release}. + * + * @param [in] instance A pointer to the original `PARCByteArray` instance. + * + * @return The value of the input parameter @p instance. + * + * Example: + * @code + * { + * PARCByteArray *x = parcByteArray_Allocate(10); + * + * PARCByteArray *x_2 = parcByteArray_Acquire(x); + * + * parcByteArray_Release(&x); + * parcByteArray_Release(&x_2); + * } + * @endcode + * + * @see parcByteArray_Release + */ +PARCByteArray *parcByteArray_Acquire(const PARCByteArray *instance); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] byteArrayPtr A pointer to a pointer to the instance of `PARCByteArray` to release. + * + * Example: + * @code + * { + * PARCByteArray *x = parcByteArray_Acquire(...); + * + * parcByteArray_Release(&x); + * } + * @endcode + */ +void parcByteArray_Release(PARCByteArray **byteArrayPtr); + +/** + * Put an `uint8_t` value into the byte array at the given index. + * + * The value of @p index must be greater than or equal to 0, and less than the capacity. + * + * @param [in,out] result A pointer to the instance of `PARCByteArray` that will receive the data. + * @param [in] index The index at which the byte will be inserted. + * @param [in] byte The byte to be inserted into the array. + * + * @return The `PARCByteArray` that receive the data. + * + * Example: + * @code + * { + * uint8_t array[10]; + * PARCByteArray *byteArray = parcByteArray_Wrap(array, 10); + * + * parcByteArray_PutByte(byteArray, 5, 123); + * + * parcByteArray_Release(&byteArray); + * } + * @endcode + */ +PARCByteArray *parcByteArray_PutByte(PARCByteArray *result, size_t index, uint8_t byte); + +/** + * Get the value at a specific index from the given `PARCByteArray`. + * + * @param [in] result The instance of `PARCByteArray` that will produce the data. + * @param [in] index The index from which the byte will be retrieved. + * + * @return The value at a specific index from the given `PARCByteArray`. + * + * Example: + * @code + * { + * uint8_t array[10]; + * PARCByteArray *byteArray = parcByteArray_Wrap(array, 10); + * + * parcByteArray_PutByte(byteArray, 5, 123); + * + * parcByteArray_GetByte(byteArray, 5); + * + * parcByteArray_Release(&byteArray); + * } + * @endcode + */ +uint8_t parcByteArray_GetByte(const PARCByteArray *result, size_t index); + +/** + * Compares instance a with instance b for order. + * + * Returns a negative integer, zero, or a positive integer as instance a is less than, equal to, or greater than instance b. + * + * @param [in] a A pointer to a `PARCByteArray` instance 'a'. + * @param [in] b A pointer to another `PARCByteArray` instance 'b'. + * + * @return <0 Instance a is less then instance b. + * @return 0 Instance a and instance b compare the same. + * @return >0 Instance a is greater than instance b. + * + * Example: + * @code + * { + * uint8_t array[10]; + * PARCByteArray *byteArray1 = parcByteArray_Wrap(array, 10); + * PARCByteArray *byteArray2 = parcByteArray_Wrap(array, 10); + * + * if (parcByteArray_Compare(byteArray1, byteArray2) == 0) { + * printf("Equal\n"); + * } + * + * parcByteArray_Release(&byteArray1); + * parcByteArray_Release(&byteArray2); + * } + * @endcode + * + * @see parcByteArray_Equal + */ +int parcByteArray_Compare(const PARCByteArray *a, const PARCByteArray *b); + +/** + * Copy data from an external array into a `PARCByteArray`. + * + * Provided that the underlying `PARCByteArray` is large enough, + * copy the bytes from the given `array` for `length` bytes. + * + * @param [in,out] result The `PARCByteArray` that will receive the data. + * @param [in] offset The offset into the `PARCByteArray` that will receive the data. + * @param [in] length The number of bytes to copy in. + * @param [in] source The uint8_t array containing the original data. + * + * @return The given `PARCByteArray` pointer + * + * @throws SIGABRT `trapOutOfBounds` if the underlying `PARCByteArray` is not large enough + * + * Example: + * @code + * { + * uint8_t array[10]; + * PARCByteArray *byteArray = parcByteArray_Wrap(array, 10); + * uint8_t inner[3]; + * inner[0] = 0xFF; + * inner[1] = 0xFF; + * inner[2] = 0xFF; + * + * parcByteArray_PutBytes(byteArray, 1, 3, inner); + * // Bytes 1,2,3 of byteArray will be 0xFF (offset was 1, not 0) + * + * parcByteArray_Release(&byteArray); + * } + * @endcode + */ +PARCByteArray *parcByteArray_PutBytes(PARCByteArray *result, size_t offset, size_t length, const uint8_t *source); + +/** + * Copy data from a `PARCByteArray` into an external array. + * + * Provided that the underlying `PARCByteArray` has at least `length` bytes + * copy the bytes from the `PARCByteArray` into the given `array`. + * + * @param [in] source The `PARCByteArray` that will produce the data. + * @param [in] offset The offset into the `PARCByteArray` from which the data will be copied. + * @param [in] length The number of bytes to copy from the <code>PARCByteArray</code> into @p destination. + * @param [in] destination The `uint8_t` array That will contain the data. + * + * @return The given `PARCByteArray` @p source pointer. + * + * Example: + * @code + * { + * uint8_t array[10]; + * PARCByteArray *byteArray = parcByteArray_Wrap(array, 10); + * + * uint8_t inner[3]; + * parcByteArray_GetBytes(byteArray, 0, 3, inner); + * // inner will contain a copy of bytes 0,1,2 from the byteArray + * + * parcByteArray_Release(&byteArray); + * } + * @endcode + */ +PARCByteArray *parcByteArray_GetBytes(const PARCByteArray *source, size_t offset, size_t length, uint8_t *destination); + + +/** + * Copy a portion of one `PARCByteArray` into another. + * + * The sum of (destination/source) offset and length must be within the bounds of the + * respective (destination/source) `PARCByteArray` instances. + * + * @param [in,out] destination Pointer to the destination `PARCByteArray` instance. + * @param [in] destOffset Offset within the destination `PARCByteArray` instance. + * @param [in] source Pointer to the source `PARCByteArray` instance. + * @param [in] srcOffset Offset within the source `PARCByteArray` instance. + * @param [in] length Number of bytes to copy from the source to the destination. + * + * @return PARCByteArray* A pointer to the destination. + * + * Example: + * @code + * { + * uint8_t array1[10]; + * PARCByteArray *byteArray1 = parcByteArray_Wrap(array1, 10); + * uint8_t array2[3]; + * array2[0] = 0xAA; + * array2[1] = 0xBB; + * array2[2] = 0xCC; + * PARCByteArray *byteArray2 = parcByteArray_Wrap(array2, 3); + * + * parcByteArray_ArrayCopy(byteArray1, 0, byteArray2, 0, 3); + * // the first three bytes of byteArray1 will now be 0xAA,0xBB,0xCC + * } + * @endcode + */ +PARCByteArray *parcByteArray_ArrayCopy(PARCByteArray *destination, size_t destOffset, const PARCByteArray *source, size_t srcOffset, size_t length); + +/** + * Get the capacity of a `PARCByteArray`. + * + * The `PARCByteArray`'s capacity is the number of bytes stored in the backing store of a `PARCByteArray`. + * + * @param [in] byteArray Pointer to the `PARCByteArray` instance being examined. + * + * @return The capacity of a `PARCByteArray`. + * + * Example: + * @code + * { + * uint8_t array[10]; + * PARCByteArray *byteArray = parcByteArray_Wrap(array, 10); + * + * size_t capacity = parcByteArray_Capacity(byteArray); + * } + * @endcode + */ +size_t parcByteArray_Capacity(const PARCByteArray *byteArray); + +/** + * Create a copy of an existing `PARCByteArray`. + * + * The copy is equal to, but shares nothing in common with, the original `PARCByteArray` + * + * @param [in] original A non-null pointer to the `PARCByteArray` to copy. + * + * @return NULL Memory could not be allocated. + * @return non-NULL A pointer to the new `PARCByteArray` instance. + * + * Example: + * @code + * { + * uint8_t array[10]; + * PARCByteArray *byteArray = parcByteArray_Wrap(array, 10); + * PARCByteArray *copy = parcByteArray_Copy(byteArray); + * } + * @endcode + * + * @see parcByteArray_Allocate + * @see parcByteArray_Wrap + */ +PARCByteArray *parcByteArray_Copy(const PARCByteArray *original); + +/** + * Determine if two `PARCByteArray` instances are equal. + * + * Two `PARCByteArray` instances are equal if, and only if, + * * They have the same number of elements, and + * * The two sequences of remaining elements are pointwise equal. + * + * The following equivalence relations on non-null `PARCByteArray` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, `parcByteArray_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `parcByteArray_Equals(x, y)` must return true if and only if + * `parcByteArray_Equals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcByteArray_Equals(x, y)` returns true and + * `parcByteArray_Equals(y, z)` returns true, + * then `parcByteArray_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `parcByteArray_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `parcByteArray_Equals(x, NULL)` must return false. + * + * @param [in] a A pointer to a `PARCByteArray` instance. + * @param [in] b A pointer to a `PARCByteArray` instance. + * + * @return true if the two `PARCByteArray` instances are equal. + * + * Example: + * @code + * { + * uint8_t array[10]; + * PARCByteArray *byteArray1 = parcByteArray_Wrap(array, 10); + * PARCByteArray *byteArray2 = parcByteArray_Wrap(array, 10); + * + * if (parcByteArray_Equals(byteArray1, byteArray2)) { + * printf("Equal\n"); + * } else { + * printf("NOT Equal\n"); + * } + * + * parcByteArray_Release(&byteArray1); + * parcByteArray_Release(&byteArray2); + * } + * @endcode + */ +bool parcByteArray_Equals(const PARCByteArray *a, const PARCByteArray *b); + +/** + * Returns a hash code value for the given instance. + * + * The general contract of `HashCode` is: + * + * Whenever it is invoked on the same instance more than once during an execution of an application, + * the {@link parcByteArray_HashCode} function must consistently return the same value, + * provided no information used in a corresponding {@link parcByteArray_Equals} + * comparisons on the instance is modified. + * + * This value need not remain consistent from one execution of an application to another execution of the same application. + * If two instances are equal according to the {@link parcByteArray_Equals} method, + * then calling the {@link parcByteArray_HashCode} method on each of the two instances must produce the same integer result. + * + * It is not required that if two instances are unequal according to the {@link parcByteArray_Equals} function, + * then calling the {@link parcByteArray_HashCode} + * method on each of the two objects must produce distinct integer results. + * + * @param [in] array A pointer to the `PARCByteArray` instance. + * + * @return The hashcode for the given instance. + * + * Example: + * @code + * { + * PARCByteArray *array = parcByteArray_Allocate(10); + * uint32_t hashValue = parcByteArray_HashCode(array); + * parcByteArray_Release(&buffer); + * } + * @endcode + * + * @see parcByteArray_HashCode + */ +PARCHashCode parcByteArray_HashCode(const PARCByteArray *array); + +/** + * Return the memory address as a `uint8_t *` to the location specified by `index`. + * + * @param [in] array A pointer to the `PARCByteArray` instance. + * @param [in] index The index of the address. + * + * @return A pointer to the location offset by index bytes within the array. + * + * Example: + * @code + * { + * PARCByteArray *byteArray = parcByteArray_Allocate(10); + * uint8_t *offsetAddress = parcByteArray_AddressOfIndex(byteArray, 4); + * // offsetAddress now points to the array at offset 4 within byteArray + * } + * @endcode + */ +uint8_t *parcByteArray_AddressOfIndex(const PARCByteArray *array, size_t index); + +/** + * Pretty print the given `PARCByteArray` instance. + * + * @param [in] indentation The amount of indentation to prefix each line of output + * @param [in] array The `PARCByteArray` instance to be printed. + * + * Example: + * @code + * { + * PARCByteArray *byteArray = parcByteArray_Allocate(10); + * parcByteArray_Display(byteArray, 0); + * } + * @endcode + */ +void parcByteArray_Display(const PARCByteArray *array, int indentation); +#endif // libparc_parc_ByteArray_h diff --git a/libparc/parc/algol/parc_CMacro.h b/libparc/parc/algol/parc_CMacro.h new file mode 100644 index 00000000..a47129ea --- /dev/null +++ b/libparc/parc/algol/parc_CMacro.h @@ -0,0 +1,49 @@ +/* + * 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. + */ + +/** + * @file parc_CMacro.h + * @ingroup Macro + * @brief Utility C-Macros + * + * CMacro contains a set of utility macros for doing complicated macro processing + * + */ +#ifndef libparc_parc_CMacro_h +#define libparc_parc_CMacro_h + +/** + * parcCMacro_ThridParam expands to the "3rd" input parameter whatever that may be. It is a part + * of the c-macro trick for implement a macro If-Else switch. If the first argument expands + * to a comma then this macro expands to the second parameter, otherwise it expands to the 3rd parameter. + */ +#define parcCMacro_ThirdParam(A, B, C, ...) C + +/** + * parcCMacro_IfElse is a c-macro trick for implementing a macro If-Else switch. + * It uses parcCMacro_ThirdParam to select between A or B depending on whether __VA_ARGS__ expands to a comma. + */ +#define parcCMacro_IfElse(A, B, ...) parcCMacro_ThirdParam(__VA_ARGS__, A, B, NOOP) + +/** \cond */ +#define _parcCMacro_Cat_(A, B) A ## B +/** \endcond */ + +/** + * parcCMacro_Cat combines two strings into a single token. + */ +#define parcCMacro_Cat(A, B) _parcCMacro_Cat_(A, B) + +#endif //libparc_parc_CMacro_h diff --git a/libparc/parc/algol/parc_Chunker.c b/libparc/parc/algol/parc_Chunker.c new file mode 100755 index 00000000..b84ed05f --- /dev/null +++ b/libparc/parc/algol/parc_Chunker.c @@ -0,0 +1,75 @@ +/* + * 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 <stdio.h> +#include <string.h> +#include <sys/time.h> + +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_Memory.h> + +#include <parc/algol/parc_Chunker.h> + +struct parc_chunker { + PARCObject *instance; + const PARCChunkerInterface *interface; +}; + +static void +_destroy(PARCChunker **chunkerP) +{ + parcObject_Release(&(*chunkerP)->instance); +} + + +parcObject_ExtendPARCObject(PARCChunker, _destroy, NULL, NULL, NULL, NULL, NULL, NULL); +parcObject_ImplementAcquire(parcChunker, PARCChunker); +parcObject_ImplementRelease(parcChunker, PARCChunker); + +PARCChunker * +parcChunker_Create(PARCObject *instance, PARCChunkerInterface *interface) +{ + PARCChunker *chunker = parcObject_CreateInstance(PARCChunker); + + if (chunker != NULL) { + chunker->instance = parcObject_Acquire(instance); + chunker->interface = interface; + } + + return chunker; +} + +PARCIterator * +parcChunker_ForwardIterator(const PARCChunker *chunker) +{ + return (chunker->interface)->ForwardIterator(chunker->instance); +} + +PARCIterator * +parcChunker_ReverseIterator(const PARCChunker *chunker) +{ + return (chunker->interface)->ReverseIterator(chunker->instance); +} + +size_t +parcChunker_GetChunkSize(const PARCChunker *chunker) +{ + return (chunker->interface)->GetChunkSize(chunker->instance); +} diff --git a/libparc/parc/algol/parc_Chunker.h b/libparc/parc/algol/parc_Chunker.h new file mode 100755 index 00000000..305acad7 --- /dev/null +++ b/libparc/parc/algol/parc_Chunker.h @@ -0,0 +1,250 @@ +/* + * 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. + */ + +/** + * @file parc_Chunker.h + * @ingroup ContentObject + * @brief A Chunker is an object that breaks up a large piece of data from a `PARCBuffer` + * or a file and provides an iterator to walk over the chunks in sequential order. + * + */ + +#ifndef libparc_parc_Chunker_h +#define libparc_parc_Chunker_h + +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_Iterator.h> + +struct parc_chunker; +/** + * @typedef PARCChunker + * @brief The PARC Chunker + */ +typedef struct parc_chunker PARCChunker; + +typedef struct PARCChunkerInterface { + /** + * @see parcChunker_ForwardIterator + */ + void *(*ForwardIterator)(const void *original); + + /** + * @see parcChunker_ReverseIterator + */ + void *(*ReverseIterator)(const void *original); + + /** + * @see parcChunker_GetChunkSize + */ + size_t (*GetChunkSize)(const void *original); +} PARCChunkerInterface; + +/** + * Create a new chunker from the given concrete instance. + * + * @param [in] instance A `PARCChunker` instance. + * @param [in] interface The interface implementation of the chunker. + * + * @retval PARCChunker A newly allocated `PARCChunker` + * @retval NULL An error occurred. + * + * Example + * @code + * { + * PARCBuffer *dataToChunk = ... + * PARCChunkerBuffer *bufferChunker = parcBufferChunker_Create(dataToChunk, 32); + * PARCChunker *chunker = parcChunker_Create(bufferCunker, &PARCBufferChunkerAsChunker); + * } + */ +PARCChunker *parcChunker_Create(PARCObject *instance, PARCChunkerInterface *interface); + +/** + * Create a new chunker to segment data contained in a file. + * + * @param [in] fileName The name of a file from which to read. + * @param [in] chunkSize The size per chunk. + * + * @retval PARCChunker A newly allocated `PARCChunker` + * @retval NULL An error occurred. + * + * Example + * @code + * { + * char *bigFileName = "big_file.bin"; + * PARCChunker *chunker = parcChunker_CreateFromBuffer(bigFileName, 32); + * } + */ +//PARCChunker *parcChunker_CreateFromFile(char *fileName, size_t maxDataSize); + +/** + * Increase the number of references to a `PARCChunker` instance. + * + * Note that new `PARCChunker` is not created, + * only that the given `PARCChunker` reference count is incremented. + * Discard the reference by invoking {@link parcChunker_Release}. + * + * @param [in] chunker A pointer to the original `PARCChunker`. + * @return The value of the input parameter @p chunker. + * + * Example: + * @code + * { + * PARCChunker *original = ... + * + * PARCChunker *reference = parcChunker_Acquire(original); + * + * parcChunker_Release(&original); + * parcChunker_Release(&reference); + * } + * @endcode + * + * @see parcChunker_Release + */ +PARCChunker *parcChunker_Acquire(const PARCChunker *chunker); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] chunkerP A pointer to a pointer to the instance to release. + * + * + * Example: + * @code + * { + * ... + * + * PARCChunker *chunker = parcChunker_Acquire(instance); + * + * parcChunker_Release(&chunker); + * } + * @endcode + */ +void parcChunker_Release(PARCChunker **chunkerP); + +/** + * Determine if two `PARCChunker` instances are equal. + * + * The following equivalence relations on non-null `PARCChunker` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, `parcChunker_Equals(x, x)` + * must return true. + * + * * It is symmetric: for any non-null reference values x and y, + * `parcChunker_Equals(x, y)` must return true if and only if + * `parcChunker_Equals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcChunker_Equals(x, y)` returns true and + * `parcChunker_Equals(y, z)` returns true, + * then `parcChunker_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple + * invocations of `parcChunker_Equals(x, y)` consistently return true or + * consistently return false. + * + * * For any non-null reference value x, `parcChunker_Equals(x, NULL)` must + * return false. + * + * @param chunkerA A pointer to a `PARCChunker` instance. + * @param chunkerB A pointer to a `PARCChunker` instance. + * @return true if the two `PARCChunker` instances are equal. + * + * Example: + * @code + * { + * PARCBuffer *dataToChunk = ... + * PARCChunker *chunkerA = ... + * PARCChunker *chunkerB = ... + * + * bool equals = parcChunker_Equals(chunkerA, chunkerB); + * } + * @endcode + */ +bool parcChunker_Equals(const PARCChunker *chunkerA, const PARCChunker *chunkerB); + +/** + * Return an iterator to traverse the chunks of the underlying data in sequential order. + * + * This function can only be called once per chunker instance since the iterator + * will mutate internal state of the chunker. + * + * @param [in] chunker A `PARCChunker` instance. + * + * @return a `PARCIterator` that traverses the chunks of the underlying data. + * + * Example + * @code + * { + * PARCBuffer *dataToChunk = ... + * PARCBufferChunker *chunker = ... + * + * PARCIterator *itr = parcChunker_ForwardIterator(chunker); + * + * // use the iterator to traverse the chunker + * } + * @endcode + */ +PARCIterator *parcChunker_ForwardIterator(const PARCChunker *chunker); + +/** + * Return an iterator to traverse the chunks of the underlying data in sequential order. + * + * This function can only be called once per chunker instance since the iterator + * will mutate internal state of the chunker. + * + * @param [in] chunker A `PARCChunker` instance. + * + * @return a `PARCIterator` that traverses the chunks of the underlying data. + * + * Example + * @code + * { + * PARCBuffer *dataToChunk = ... + * PARCChunker *chunker = ... + * + * PARCIterator *itr = parcChunker_ReverseIterator(chunker); + * + * // use the iterator to traverse the chunker + * } + * @endcode + */ +PARCIterator *parcChunker_ReverseIterator(const PARCChunker *chunker); + +/** + * Get the chunk size of a `PARCChunker.` + * + * @param [in] chunker A `PARCChunker` instance. + * + * @return the chunk size + * + * Example + * @code + * { + * PARCBuffer *dataToChunk = ... + * PARCChunker *chunker = ... + * + * size_t chunkSize = parcChunker_GetChunkSize(chunker); + * } + * @endcode + */ +size_t parcChunker_GetChunkSize(const PARCChunker *chunker); +#endif // libparc_parc_Chunker_h diff --git a/libparc/parc/algol/parc_Clock.c b/libparc/parc/algol/parc_Clock.c new file mode 100755 index 00000000..95cf74bc --- /dev/null +++ b/libparc/parc/algol/parc_Clock.c @@ -0,0 +1,245 @@ +/* + * 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 <time.h> +#include <parc/algol/parc_Clock.h> + +#if __APPLE__ +#include <mach/mach.h> +#include <mach/clock.h> +#include <mach/mach_time.h> +#endif + +// These are used by the counter Clock +#include <parc/algol/parc_AtomicInteger.h> +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_Memory.h> +// ---- + +typedef struct counter_clock { + uint64_t counter; +} _CounterClock; + +parcObject_ExtendPARCObject(_CounterClock, NULL, NULL, NULL, NULL, NULL, NULL, NULL); +static parcObject_ImplementAcquire(_counterClock, _CounterClock); + +static PARCClock * +_counterClock_AcquireInterface(const PARCClock *clock) +{ + _CounterClock *cc = (_CounterClock *) clock->closure; + _counterClock_Acquire(cc); + return (PARCClock *) clock; +} + +static void +_counterClock_ReleaseInterface(PARCClock **clockPtr) +{ + PARCClock *clock = *clockPtr; + _CounterClock *cc = (_CounterClock *) clock->closure; + + PARCReferenceCount refcount = parcObject_Release((void **) &cc); + if (refcount == 0) { + parcMemory_Deallocate((void **) clockPtr); + } else { + *clockPtr = NULL; + } +} + +static void +_counterClock_GetTimeval(const PARCClock *clock, struct timeval *output) +{ + _CounterClock *cc = (_CounterClock *) clock->closure; + uint64_t value = parcAtomicInteger_Uint64Increment(&cc->counter); + + // put 19 bits in the micro-seconds so it is never larger than 1E+6 + output->tv_sec = value >> 19; + output->tv_usec = value & 0x7FFFF; +} + +static uint64_t +_counterClock_GetTime(const PARCClock *clock) +{ + _CounterClock *cc = (_CounterClock *) clock->closure; + return parcAtomicInteger_Uint64Increment(&cc->counter); +} + +PARCClock * +parcClock_Counter(void) +{ + _CounterClock *cc = parcObject_CreateInstance(_CounterClock); + cc->counter = 0; + + PARCClock *clock = parcMemory_Allocate(sizeof(PARCClock)); + clock->closure = cc; + clock->acquire = _counterClock_AcquireInterface; + clock->release = _counterClock_ReleaseInterface; + clock->getTime = _counterClock_GetTime; + clock->getTimeval = _counterClock_GetTimeval; + return clock; +} + +// =========== +// Wallclock + +static void +_wallclock_GetTimeval(const PARCClock *dummy __attribute__((unused)), struct timeval *output) +{ +#if __linux__ + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + output->tv_sec = ts.tv_sec; + output->tv_usec = ts.tv_nsec / 1000; +#else + clock_serv_t clockService; + mach_timespec_t mts; + + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &clockService); + clock_get_time(clockService, &mts); + mach_port_deallocate(mach_task_self(), clockService); + + output->tv_sec = mts.tv_sec; + output->tv_usec = mts.tv_nsec / 1000; +#endif +} + +static uint64_t +_wallclock_GetTime(const PARCClock *clock) +{ + struct timeval tv; + _wallclock_GetTimeval(clock, &tv); + uint64_t t = tv.tv_sec * 1000 + tv.tv_usec / 1000; + + return t; +} + + +static PARCClock * +_wallclock_Acquire(const PARCClock *clock) +{ + return (PARCClock *) clock; +} + +static void +_wallclock_Release(PARCClock **clockPtr) +{ + *clockPtr = NULL; +} + +static PARCClock _wallclock = { + .closure = NULL, + .getTime = _wallclock_GetTime, + .getTimeval = _wallclock_GetTimeval, + .acquire = _wallclock_Acquire, + .release = _wallclock_Release +}; + +PARCClock * +parcClock_Wallclock(void) +{ + return &_wallclock; +} + + +// ========================== +// Monotonic clock + +static void +_monoclock_GetTimeval(const PARCClock *dummy __attribute__((unused)), struct timeval *output) +{ +#if __linux__ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC_RAW, &ts); + output->tv_sec = ts.tv_sec; + output->tv_usec = ts.tv_nsec / 1000; +#else + clock_serv_t clockService; + mach_timespec_t mts; + + host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &clockService); + clock_get_time(clockService, &mts); + mach_port_deallocate(mach_task_self(), clockService); + + output->tv_sec = mts.tv_sec; + output->tv_usec = mts.tv_nsec / 1000; +#endif +} + +static uint64_t +_monoclock_GetTime(const PARCClock *clock) +{ + struct timeval tv; + _monoclock_GetTimeval(clock, &tv); + uint64_t t = tv.tv_sec * 1000 + tv.tv_usec / 1000; + + return t; +} + +static PARCClock * +_monoclock_Acquire(const PARCClock *clock) +{ + return (PARCClock *) clock; +} + +static void +_monoclock_Release(PARCClock **clockPtr) +{ + *clockPtr = NULL; +} + +static PARCClock _monoclock = { + .closure = NULL, + .getTime = _monoclock_GetTime, + .getTimeval = _monoclock_GetTimeval, + .acquire = _monoclock_Acquire, + .release = _monoclock_Release +}; + +PARCClock * +parcClock_Monotonic(void) +{ + return &_monoclock; +} + +// =========================== +// Facade API + +uint64_t +parcClock_GetTime(const PARCClock *clock) +{ + return clock->getTime(clock); +} + +void +parcClock_GetTimeval(const PARCClock *clock, struct timeval *output) +{ + clock->getTimeval(clock, output); +} + +PARCClock * +parcClock_Acquire(const PARCClock *clock) +{ + return clock->acquire(clock); +} + +void +parcClock_Release(PARCClock **clockPtr) +{ + (*clockPtr)->release(clockPtr); +} + diff --git a/libparc/parc/algol/parc_Clock.h b/libparc/parc/algol/parc_Clock.h new file mode 100755 index 00000000..35c77fd7 --- /dev/null +++ b/libparc/parc/algol/parc_Clock.h @@ -0,0 +1,354 @@ +/* + * 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. + */ + +/** + * @file parc_Clock.h + * @ingroup datastructures + * @brief Generic API to a clock + * + * Interface over clock providers. We provide two system clocks, a Wallclock that tracks the + * real time clock and a monotonic clock that will not skew or go backwards. + * @see parcClock_Monotonic() + * and + * @see parcClock_Wallclock() + * Also provided is a counting clock. + * @see parcClock_Counter() + * + * Below is a complete example of a simple custom clock that implements an atomic counter for + * the time. Each call to getTime or getTimeval will increment the counter. This clock is + * available as parcClock_Counter(). + * + * @code + * typedef struct counter_clock { + * uint64_t counter; + * } _CounterClock; + * + * parcObject_ExtendPARCObject(_CounterClock, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + * static parcObject_ImplementAcquire(_counterClock, _CounterClock); + * + * static PARCClock * + * _counterClock_AcquireInterface(const PARCClock *clock) + * { + * _CounterClock *cc = (_CounterClock *) clock->closure; + * _counterClock_Acquire(cc); + * return (PARCClock *) clock; + * } + * + * static void + * _counterClock_ReleaseInterface(PARCClock **clockPtr) + * { + * PARCClock *clock = *clockPtr; + * _CounterClock *cc = (_CounterClock *) clock->closure; + * + * PARCReferenceCount refcount = parcObject_Release((void **) &cc); + * if (refcount == 0) { + * parcMemory_Deallocate((void **) clockPtr); + * } else { + * *clockPtr = NULL; + * } + * } + * + * static void + * _counterClock_GetTimeval(const PARCClock *clock, struct timeval *output) + * { + * _CounterClock *cc = (_CounterClock *) clock->closure; + * uint64_t value = parcAtomicInteger_Uint64Increment(&cc->counter); + * // put 19 bits in the micro-seconds so it is never larger than 1E+6 + * output->tv_sec = value >> 19; + * output->tv_usec = value & 0x7FFFF; + * } + * + * static uint64_t + * _counterClock_GetTime(const PARCClock *clock) + * { + * _CounterClock *cc = (_CounterClock *) clock->closure; + * return parcAtomicInteger_Uint64Increment(&cc->counter); + * } + * + * PARCClock * + * parcClock_Counter(void) + * { + * _CounterClock *cc = parcObject_CreateInstance(_CounterClock); + * cc->counter = 0; + * + * PARCClock *clock = parcMemory_Allocate(sizeof(PARCClock)); + * clock->closure = cc; + * clock->acquire = _counterClock_AcquireInterface; + * clock->release = _counterClock_ReleaseInterface; + * clock->getTime = _counterClock_GetTime; + * clock->getTimeval = _counterClock_GetTimeval; + * return clock; + * } + * @encode + * + */ + +#ifndef PARC_parc_Clock_h +#define PARC_parc_Clock_h + +#include <inttypes.h> +#include <sys/time.h> + +struct parc_clock; +typedef struct parc_clock PARCClock; + +struct parc_clock { + /** + * Opaque parameter set by the clock provider + */ + void *closure; + + /** + * Gets the clock time + * + * The resolution and epoch of the clock are determined by the clock provider + * + * @param [in] clock An allocated PARCClock + * + * @retval number The clock's time as a uint64_t + * + * Example: + * @code + * { + * PARCClock *clock = parcClock_Monotonic(); + * uint64_t t = parcClock_GetTime(clock); + * parcClock_Release(&clock); + * } + * @endcode + */ + uint64_t (*getTime)(const PARCClock *clock); + + /** + * Gets the clock time as a struct timeval + * + * The resolution and epoch of the clock are determined by the clock provider. + * There may be an arbitrary mapping to the struct timeval as per the clock + * provider, so the units of 'seconds' and 'micro seconds' need to be interpreted + * as per the clock provider. + * + * @param [in] clock An allocated PARCClock + * + * Example: + * @code + * <#example#> + * @endcode + */ + void (*getTimeval)(const PARCClock *clock, struct timeval *output); + + /** + * Increase the number of references to a `PARCClock`. + * + * Note that new `PARCClock` is not created, + * only that the given `PARCClock` reference count is incremented. + * Discard the reference by invoking `parcClock_Release`. + * + * @param [in] clock A pointer to a `PARCClock` instance. + * + * @return The input `PARCClock` pointer. + * + * Example: + * @code + * { + * PARCClock *clock = parcClock_Counter(); + * PARCClock *copy = parcClock_Acquire(clock); + * parcClock_Release(©); + * parcClock_Release(&clock); + * + * } + * @endcode + */ + PARCClock * (*acquire)(const PARCClock *clock); + + /** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] clockPtr A pointer to a pointer to the instance to release. + * + * Example: + * @code + * { + * PARCClock *clock = parcClock_Counter(); + * PARCClock *copy = parcClock_Acquire(clock); + * parcClock_Release(©); + * parcClock_Release(&clock); + * + * } + * @endcode + */ + + void (*release)(PARCClock **clockPtr); +}; + +/** + * A clock provider for the wall clock time + * + * This will use clock_gettime(CLOCK_REALTIME_COARSE) on linux or + * host_get_clock_service with CALENDAR_CLOCK on Mac + * + * @retval non-null An allocated PARCClock, which must be released via parcClock_Release. + * + * Example: + * @code + * { + * PARCClock *clock = parcClock_Wallclock(); + * uint64_t t = parcClock_GetTime(clock); + * parcClock_Release(&clock); + * } + * @endcode + */ +PARCClock *parcClock_Wallclock(void); + +/** + * A monotonic clock that will not normally adjust for time changes + * + * On linux, this uses the CLOCK_MONOTONIC_RAW. On Darwin, it uses + * the SYSTEM_CLOCK from <mach/mach_time.h>. + * + * @retval non-null An allocated PARCClock, which must be released via parcClock_Release. + * + * Example: + * @code + * { + * PARCClock *clock = parcClock_Monotonic(); + * uint64_t t = parcClock_GetTime(clock); + * parcClock_Release(&clock); + * } + * @endcode + */ +PARCClock *parcClock_Monotonic(void); + + +/** + * The counter clock begins at 0 and increments for every call to getTime or getTimeval + * + * Each allocated counter clock will begin at zero. Copies made via parcClock_Acquire() will + * share the same counter and use atomic updates. + * + * getTime() will return the counter. + * + * getTimeval() will return the lower 19 bits in tv_usec (so it does not overflow the concept + * of micro-second) and the upper 45 bits are in tv_sec. On some platforms, that may overflow. + * + * @retval non-null An allocated PARCClock, which must be released via parcClock_Release. + * + * Example: + * @code + * { + * PARCClock *clock = parcClock_Counter(); + * uint64_t t = parcClock_GetTime(clock); + * parcClock_Release(&clock); + * } + * @endcode + */ +PARCClock *parcClock_Counter(void); + +/** + * Returns the clock provider's idea of the current time as a uint64 + * + * Returns the clock provider's idea of the current time, which may or + * may not be a wall clock time. + * + * @param [in] clock A clock provider + * + * @retval number The current time (depends on provider) + * + * Example: + * @code + * { + * PARCClock *clock = parcClock_Counter(); + * uint64_t t = parcClock_GetTime(clock); + * parcClock_Release(&clock); + * } + * @endcode + */ +uint64_t parcClock_GetTime(const PARCClock *clock); + +/** + * Returns the clock provider's idea of the current time as a timeval + * + * Returns the clock provider's idea of the current time, which may or + * may not be a wall clock time. + * + * @param [in] clock A clock provider + * @param [in] output The structure to fill in with the current time + * + * Example: + * @code + * { + * struct timeval t; + * PARCClock *clock = parcClock_Counter(); + * parcClock_GetTimeval(clock, &t); + * parcClock_Release(&clock); + * } + * @endcode + */ +void parcClock_GetTimeval(const PARCClock *clock, struct timeval *output); + +/** + * Increase the number of references to a `PARCClock`. + * + * Note that new `PARCClock` is not created, + * only that the given `PARCClock` reference count is incremented. + * Discard the reference by invoking `parcClock_Release`. + * + * @param [in] clock A pointer to a `PARCClock` instance. + * + * @return The input `PARCClock` pointer. + * + * Example: + * @code + * { + * PARCClock *clock = parcClock_Counter(); + * PARCClock *copy = parcClock_Acquire(clock); + * parcClock_Release(©); + * parcClock_Release(&clock); + * + * } + * @endcode + */ +PARCClock *parcClock_Acquire(const PARCClock *clock); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] clockPtr A pointer to a pointer to the instance to release. + * + * Example: + * @code + * { + * PARCClock *clock = parcClock_Counter(); + * PARCClock *copy = parcClock_Acquire(clock); + * parcClock_Release(©); + * parcClock_Release(&clock); + * + * } + * @endcode + */ +void parcClock_Release(PARCClock **clockPtr); +#endif // PARC_parc_Clock_h diff --git a/libparc/parc/algol/parc_Collection.h b/libparc/parc/algol/parc_Collection.h new file mode 100755 index 00000000..87deff12 --- /dev/null +++ b/libparc/parc/algol/parc_Collection.h @@ -0,0 +1,32 @@ +/* + * 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. + */ + +/** + * @file parc_Collection.h + * @ingroup datastructures + * + * + * Example: + * @code + * <#example#> + * @endcode + */ + +#ifndef libparc_parc_Collection_h +#define libparc_parc_Collection_h + +struct parc_collection; +typedef struct parc_collection PARCCollection; +#endif // libparc_parc_Collection_h diff --git a/libparc/parc/algol/parc_Deque.c b/libparc/parc/algol/parc_Deque.c new file mode 100644 index 00000000..4793b032 --- /dev/null +++ b/libparc/parc/algol/parc_Deque.c @@ -0,0 +1,446 @@ +/* + * 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 <LongBow/runtime.h> + +#include <stdio.h> +#include <sys/queue.h> + +#include <parc/algol/parc_Deque.h> + +#include <parc/algol/parc_DisplayIndented.h> +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_Memory.h> + +PARCListInterface *PARCDequeAsPARCList = &(PARCListInterface) { + .Add = (bool (*)(void *, void *))parcDeque_Append, + .AddAtIndex = (void (*)(void *, int index, void *))NULL, + .AddCollection = (bool (*)(void *, PARCCollection *))NULL, + .AddCollectionAtIndex = (bool (*)(void *, int index, PARCCollection *))NULL, + .Clear = (void (*)(void *))NULL, + .Contains = (bool (*)(const void *, const PARCObject *))NULL, + .ContainsCollection = (bool (*)(const void *, const PARCCollection *))NULL, + .Copy = (void * (*)(const PARCList *))parcDeque_Copy, + .Destroy = (void (*)(void **))parcDeque_Release, + .Equals = (bool (*)(const void *, const void *))parcDeque_Equals, + .GetAtIndex = (void * (*)(const void *, size_t))parcDeque_GetAtIndex, + .HashCode = (PARCHashCode (*)(const void *))NULL, + .IndexOf = (size_t (*)(const void *, const PARCObject *element))NULL, + .IsEmpty = (bool (*)(const void *))parcDeque_IsEmpty, + .LastIndexOf = (size_t (*)(void *, const PARCObject *element))NULL, + .Remove = (bool (*)(void *, const PARCObject *element))NULL, + .RemoveAtIndex = (void * (*)(PARCList *, size_t))NULL, + .RemoveCollection = (bool (*)(void *, const PARCCollection *))NULL, + .RetainCollection = (bool (*)(void *, const PARCCollection *))NULL, + .SetAtIndex = (void * (*)(void *, size_t index, void *))NULL, + .Size = (size_t (*)(const void *))parcDeque_Size, + .SubList = (PARCList * (*)(const void *, size_t, size_t))NULL, + .ToArray = (void** (*)(const void *))NULL, +}; + +struct parc_deque_node { + void *element; + struct parc_deque_node *previous; + struct parc_deque_node *next; +}; + +struct parc_deque { + PARCObjectDescriptor object; + struct parc_deque_node *head; + struct parc_deque_node *tail; + size_t size; +}; + +static void * +_defaultElementCopy(const void *x) +{ + return (void *) x; +} + +static bool +_defaultElementEquals(const void *x, const void *y) +{ + return (x == y); +} + +static inline struct parc_deque_node * +_parcDequeNode_Create(void *element, struct parc_deque_node *previous, struct parc_deque_node *next) +{ + struct parc_deque_node *result = parcMemory_Allocate(sizeof(struct parc_deque_node)); + if (result != NULL) { + result->element = element; + result->next = next; + result->previous = previous; + } + + return result; +} + +static void +_parcDequeNode_Destroy(PARCDeque *deque __attribute__((unused)), struct parc_deque_node **nodePtr) +{ + struct parc_deque_node *node = *nodePtr; + + parcMemory_Deallocate((void **) &node); + *nodePtr = 0; +} + +static void +_parcDequeNode_AssertInvariants(struct parc_deque_node *node) +{ + assertNotNull(node, "Expected non-null node pointer."); + if (node->next != NULL) { + assertTrue(node->next->previous == node, "Expected next node to point to this node."); + } + if (node->previous != NULL) { + assertTrue(node->previous->next == node, "Expected previous node to point to this node."); + } +} + +static void +_parcDeque_AssertInvariants(const PARCDeque *deque) +{ + assertNotNull(deque, "Parameter cannot be null."); + if (deque->head != NULL) { + assertTrue(deque->size != 0, "PARCDeque head is not-null, but size is zero."); + assertNotNull(deque->tail, "PARCDeque head is not-null, but tail is null."); + _parcDequeNode_AssertInvariants(deque->head); + _parcDequeNode_AssertInvariants(deque->tail); + } else { + assertNull(deque->tail, "PARCDeque head is null, but tail is not null."); + assertTrue(deque->size == 0, "PARCDeque head is null, but size is not zero."); + } +} + +static void +_parcDeque_Destroy(PARCDeque **dequePtr) +{ + PARCDeque *deque = *dequePtr; + + struct parc_deque_node *next = NULL; //deque->head; + + for (struct parc_deque_node *node = deque->head; node != NULL; node = next) { + next = node->next; + _parcDequeNode_Destroy(deque, &node); + } +} + +static struct parc_deque_node * +_parcDequeNode_Init(PARCDeque *deque __attribute__((unused))) +{ + return NULL; +} + +static bool +_parcDequeNode_Fini(PARCDeque *deque __attribute__((unused)), const struct parc_deque_node *node __attribute__((unused))) +{ + return true; +} + +static struct parc_deque_node * +_parcDequeNode_Next(PARCDeque *deque __attribute__((unused)), const struct parc_deque_node *node) +{ + if (node == NULL) { + return deque->head; + } + trapOutOfBoundsIf(node->next == NULL, "No more elements."); + return node->next; +} + +static bool +_parcDequeNode_HasNext(PARCDeque *deque __attribute__((unused)), const struct parc_deque_node *node) +{ + if (node == NULL) { + return (deque->head != NULL); + } + return (node->next != NULL); +} + +static void * +_parcDequeNode_Element(PARCDeque *deque __attribute__((unused)), const struct parc_deque_node *node) +{ + return node->element; +} + +parcObject_ExtendPARCObject(PARCDeque, _parcDeque_Destroy, parcDeque_Copy, NULL, parcDeque_Equals, NULL, NULL, NULL); + +static PARCDeque * +_create(const PARCObjectDescriptor *interface) +{ + PARCDeque *result = parcObject_CreateInstance(PARCDeque); + + if (result != NULL) { + result->object = *interface; + result->head = NULL; + result->tail = NULL; + result->size = 0; + } + return result; +} + +PARCIterator * +parcDeque_Iterator(PARCDeque *deque) +{ + PARCIterator *iterator = parcIterator_Create(deque, + (void *(*)(PARCObject *))_parcDequeNode_Init, + (bool (*)(PARCObject *, void *))_parcDequeNode_HasNext, + (void *(*)(PARCObject *, void *))_parcDequeNode_Next, + NULL, + (void *(*)(PARCObject *, void *))_parcDequeNode_Element, + (void (*)(PARCObject *, void *))_parcDequeNode_Fini, + NULL); + + return iterator; +} + +PARCDeque * +parcDeque_Create(void) +{ + static PARCObjectDescriptor defaultObjectInterface = { + .destroy = (PARCObjectDestroy *) NULL, + .copy = (PARCObjectCopy *) _defaultElementCopy, + .toString = (PARCObjectToString *) NULL, + .equals = (PARCObjectEquals *) _defaultElementEquals, + .compare = (PARCObjectCompare *) NULL + }; + return _create(&defaultObjectInterface); +} + +PARCDeque * +parcDeque_CreateObjectInterface(const PARCObjectDescriptor *interface) +{ + return _create(interface); +} + +PARCDeque * +parcDeque_CreateCustom(bool (*elementEquals)(const void *, const void *), void *(*elementCopy)(const void *)) +{ + PARCObjectDescriptor objectInterface; + parcObject_MetaInitialize(&objectInterface); + + objectInterface.equals = elementEquals != NULL ? elementEquals : _defaultElementEquals; + objectInterface.copy = elementCopy != NULL ? elementCopy : _defaultElementCopy; + + return _create(&objectInterface); +} + +parcObject_ImplementAcquire(parcDeque, PARCDeque); + +parcObject_ImplementRelease(parcDeque, PARCDeque); + +PARCDeque * +parcDeque_Copy(const PARCDeque *deque) +{ + PARCDeque *result = _create(&deque->object); + + struct parc_deque_node *node = deque->head; + + while (node != NULL) { + parcDeque_Append(result, deque->object.copy(node->element)); + node = node->next; + } + + return result; +} + +PARCDeque * +parcDeque_Append(PARCDeque *deque, void *element) +{ + struct parc_deque_node *node = _parcDequeNode_Create(element, deque->tail, NULL); + + if (deque->tail == NULL) { + deque->tail = node; + } else { + deque->tail->next = node; + deque->tail = node; + } + + if (deque->head == NULL) { + deque->head = deque->tail; + } + + deque->size++; + + return deque; +} + +PARCDeque * +parcDeque_Prepend(PARCDeque *deque, void *element) +{ + struct parc_deque_node *node = _parcDequeNode_Create(element, NULL, deque->head); + + if (deque->head == NULL) { + deque->head = node; + } else { + deque->head->previous = node; + deque->head = node; + } + + if (deque->tail == NULL) { + deque->tail = deque->head; + } + deque->size++; + + _parcDequeNode_AssertInvariants(node); + _parcDeque_AssertInvariants(deque); + + return deque; +} + +void * +parcDeque_RemoveFirst(PARCDeque *deque) +{ + void *result = NULL; + + if (deque->head != NULL) { + struct parc_deque_node *node = deque->head; + result = node->element; + if (deque->head == deque->tail) { + deque->head = NULL; + deque->tail = NULL; + } else { + deque->head = node->next; + deque->head->previous = NULL; + } + parcMemory_Deallocate((void **) &node); + deque->size--; + } + + _parcDeque_AssertInvariants(deque); + + return result; +} + +void * +parcDeque_RemoveLast(PARCDeque *deque) +{ + void *result = NULL; + + if (deque->tail != NULL) { + struct parc_deque_node *node = deque->tail; + deque->tail = node->previous; + deque->tail->next = NULL; + + result = node->element; + parcMemory_Deallocate((void **) &node); + deque->size--; + } + + _parcDeque_AssertInvariants(deque); + return result; +} + +void * +parcDeque_PeekFirst(const PARCDeque *deque) +{ + void *result = NULL; + + if (deque->head != NULL) { + struct parc_deque_node *node = deque->head; + result = node->element; + } + return result; +} + +void * +parcDeque_PeekLast(const PARCDeque *deque) +{ + void *result = NULL; + + if (deque->tail != NULL) { + struct parc_deque_node *node = deque->tail; + result = node->element; + } + return result; +} + +size_t +parcDeque_Size(const PARCDeque *deque) +{ + return deque->size; +} + +bool +parcDeque_IsEmpty(const PARCDeque *deque) +{ + return (parcDeque_Size(deque) == 0); +} + +void * +parcDeque_GetAtIndex(const PARCDeque *deque, size_t index) +{ + if (index > (parcDeque_Size(deque) - 1)) { + trapOutOfBounds(index, "[0, %zd]", parcDeque_Size(deque) - 1); + } + struct parc_deque_node *node = deque->head; + while (index--) { + node = node->next; + } + + return node->element; +} + +bool +parcDeque_Equals(const PARCDeque *x, const PARCDeque *y) +{ + if (x == y) { + return true; + } + if (x == NULL || y == NULL) { + return false; + } + + if (x->object.equals == y->object.equals) { + if (x->size == y->size) { + struct parc_deque_node *xNode = x->head; + struct parc_deque_node *yNode = y->head; + + while (xNode != NULL) { + if (x->object.equals(xNode->element, yNode->element) == false) { + return false; + } + xNode = xNode->next; + yNode = yNode->next; + } + return true; + } + } + return false; +} + +void +parcDeque_Display(const PARCDeque *deque, const int indentation) +{ + if (deque == NULL) { + parcDisplayIndented_PrintLine(indentation, "PARCDeque@NULL"); + } else { + parcDisplayIndented_PrintLine(indentation, "PARCDeque@%p {", (void *) deque); + + struct parc_deque_node *node = deque->head; + + while (node != NULL) { + parcDisplayIndented_PrintLine(indentation + 1, + ".previous=%11p, %11p=%11p, .next=%11p", + node->previous, node, node->element, node->next); + node = node->next; + } + + parcDisplayIndented_PrintLine(indentation, "}\n"); + } +} diff --git a/libparc/parc/algol/parc_Deque.h b/libparc/parc/algol/parc_Deque.h new file mode 100755 index 00000000..3e11e845 --- /dev/null +++ b/libparc/parc/algol/parc_Deque.h @@ -0,0 +1,465 @@ +/* + * 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. + */ + +/** + * @file parc_Deque.h + * @ingroup datastructures + * @brief PARC Double-ended Queue (Deque) + * + * + */ +#ifndef libparc_parc_Deque_h +#define libparc_parc_Deque_h +#include <stdbool.h> +#include <stdint.h> + +#include <parc/algol/parc_List.h> +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_Iterator.h> + +struct parc_deque; +/** + * A double-ended queue. + * + * @see {@link parcDeque_Create} + * @see {@link parcDeque_CreateCustom} + */ +typedef struct parc_deque PARCDeque; + +/** + * Create a `PARCDeque` instance with the default element equality and copy functions. + * + * The queue is created with no elements. + * + * The default element equals function is used by the `{@link parcDeque_Equals} function and + * simply compares the values using the `==` operator. + * Users that need more sophisticated comparisons of the elements need to supply their own + * function via the {@link parcDeque_CreateCustom} function. + * + * @return non-NULL A pointer to a `PARCDeque` instance. + * + * Example: + * @code + * <#example#> + * @endcode + * + */ +PARCDeque *parcDeque_Create(void); + +PARCIterator *parcDeque_Iterator(PARCDeque *deque); + +/** + * Create a PARCDeque instance that uses the {@link PARCObjectDescriptor} providing functions for element equality and copy function. + * + * The queue is created with no elements. + * + * @param [in] interface A pointer to a `PARCObjectDescriptor` instance. + * + * @return non-NULL A pointer to a `PARCDeque` instance. + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCDeque *parcDeque_CreateObjectInterface(const PARCObjectDescriptor *interface); + +/** + * Create a `PARCDeque` instance with a custom element equality and copy function. + * + * The queue is created with no elements. + * + * The supplied element equals function is used by the `parcDeque_Equals` + * function which must return `true` if the elements are equal, and `false` if unequal. + * + * @param [in] elementEquals The function to be used for equals + * @param [in] elementCopy The function to be used for copy + * + * @return non-NULL A pointer to a `PARCDeque` instance. + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see {@link parcDeque_CreateObjectInterface} + */ +PARCDeque *parcDeque_CreateCustom(bool (*elementEquals)(const void *, const void *), void *(*elementCopy)(const void *)); + +/** + * Acquire a new reference to an instance of `PARCDeque`. + * + * The reference count to the instance is incremented. + * + * @param [in] deque The instance of `PARCDeque` to which to refer. + * + * @return The same value as the input parameter @p deque + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCDeque *parcDeque_Acquire(const PARCDeque *deque); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's interface will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] dequePtr A pointer to a pointer to the instance of `PARCDeque` to release. + * + * + * Example: + * @code + * { + * PARCDeque *buffer = parcDeque_Create(10); + * + * parcDeque_Release(&buffer); + * } + * @endcode + */ +void parcDeque_Release(PARCDeque **dequePtr); + +/** + * Copy a a `PARCDeque` to another. + * + * @param [in] deque A pointer to an instance of `PARCDeque` + * + * @return A pointer to a copy of the original instance of `PARCDeque` + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +PARCDeque *parcDeque_Copy(const PARCDeque *deque); + +/** + * Append an element to the tail end of the specified `PARCDeque` + * + * @param [in,out] deque A pointer to the instance of `PARCDeque` to which the element will be appended + * @param [in] element A pointer to the element to be appended to the instance of `PARCDeque` + * + * @return non NULL A pointer to the specific instance of `PARCDeque` + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCDeque *parcDeque_Append(PARCDeque *deque, void *element); + +/** + * Prepend an element to the head end of the specified `PARCDeque` + * + * + * @param [in,out] deque A pointer to the instance of `PARCDeque` to which the element will be prepended + * @param [in] element A pointer to the element to be appended to the instance of `PARCDeque` + * + * @return non NULL A pointer to the specific instance of `PARCDeque` + * + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCDeque *parcDeque_Prepend(PARCDeque *deque, void *element); + +/** + * Return the first element of the specified `PARCDeque` and remove it from the queue + * + * @param [in,out] deque A pointer to the instance of `PARCDeque` from which the first element will be returned and removed + * + * @return non NULL A pointer to the element removed + * + * Example: + * @code + * <#example#> + * @endcode + * + */ +void *parcDeque_RemoveFirst(PARCDeque *deque); + +/** + * Return the last element of the specified `PARCDeque` and remove it from the queue + * + * @param [in,out] deque A pointer to the instance of `PARCDeque` from which the last element will be returned and removed + * + * @return non NULL A pointer to the element removed + * + * Example: + * @code + * <#example#> + * @endcode + * + */ +void *parcDeque_RemoveLast(PARCDeque *deque); + +/** + * Return the first element of the specified `PARCDeque` but do NOT remove it from the queue + * + * @param [in] deque A pointer to the instance of `PARCDeque` from which the first element will be returned + * + * @return non NULL A pointer to the first element + * + * Example: + * @code + * <#example#> + * @endcode + * + */ +void *parcDeque_PeekFirst(const PARCDeque *deque); + +/** + * Return the last element of the specified `PARCDeque` but do NOT remove it from the queue + * + * @param [in] deque A pointer to the instance of `PARCDeque` from which the last element will be returned + * + * @return non NULL A pointer to the last element + * + * Example: + * @code + * <#example#> + * @endcode + * + */ +void *parcDeque_PeekLast(const PARCDeque *deque); + +/** + * Return the size of the specified queue + * + * @param [in] deque A pointer to the instance of `PARCDeque` + * + * @return `size_t` The size of the queue + * + * Example: + * @code + * <#example#> + * @endcode + * + */ +size_t parcDeque_Size(const PARCDeque *deque); + +/** + * Return True if the `PARCDeque` is empty or False if not. + * + * @param [in] deque A pointer to the instance of `PARCDeque` + * + * @return bool True if the `PARCDeque` is empty or False if not. + * + * Example: + * @code + * <#example#> + * @endcode + * + */ +bool parcDeque_IsEmpty(const PARCDeque *deque); + +/** + * Get a pointer to the specified element. + * + * @param [in] deque A pointer to a `PARCDeque` instance. + * @param [in] index The index of the element to be retrieved. + * + * @throws `trapOutOfBounds` + * + * Example: + * @code + * <#example#> + * @endcode + * + */ +void *parcDeque_GetAtIndex(const PARCDeque *deque, size_t index); + +/** + * Determine if two `PARCDeque` instances are equal. + * + * This function implements the following equivalence relations on non-null `PARCDeque` instances: + * + * * It is reflexive: for any non-null reference value x, `parcDeque_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `parcDeque_Equals(x, y)` must return true if and only if + * `parcDeque_Equals(y x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcDeque_Equals(x, y)` returns true and + * `parcDeque_Equals(y, z)` returns true, + * then `parcDeque_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `parcDeque_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `parcDeque_Equals(x, NULL)` must return false. + * + * Two `PARCDeque` instances with different element equality functions are always unequal. + * + * @param [in] x A pointer to a `PARCDeque` instance. + * @param [in] y A pointer to a `PARCDeque` instance. + * + * @return true `PARCDeque` x and y are equal. + * @return false `PARCDeque` x and y are not equal. + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool parcDeque_Equals(const PARCDeque *x, const PARCDeque *y); + +/** + * Print a human readable representation of the given `PARCDeque`. + * + * @param [in] indentation The level of indentation to use to pretty-print the output. + * @param [in] deque A pointer to the instance to display. + * + * Example: + * @code + * { + * PARCDeque *instance = parcDeque_Create(); + * + * parcDeque_Display(instance, 0); + * + * parcDeque_Release(&instance); + * } + * @endcode + * + */ +void parcDeque_Display(const PARCDeque *deque, int indentation); + +/** + * Wakes up a single thread that is waiting on this object (see `parcDeque_Wait)`. + * If any threads are waiting on this object, one of them is chosen to be awakened. + * The choice is arbitrary and occurs at the discretion of the underlying implementation. + * + * The awakened thread will not be able to proceed until the current thread relinquishes the lock on this object. + * The awakened thread will compete in the usual manner with any other threads that might be actively + * competing to synchronize on this object; + * for example, the awakened thread enjoys no reliable privilege or disadvantage in being the next thread to lock this object. + * + * @param [in] object A pointer to a valid PARCDeque instance. + * + * Example: + * @code + * { + * + * parcDeque_Notify(object); + * } + * @endcode + */ +parcObject_ImplementNotify(parcDeque, PARCDeque); + +/** + * Causes the calling thread to wait until either another thread invokes the parcDeque_Notify() function on the same object. + * * + * @param [in] object A pointer to a valid `PARCDeque` instance. + * + * Example: + * @code + * { + * + * parcDeque_Wait(object); + * } + * @endcode + */ +parcObject_ImplementWait(parcDeque, PARCDeque); + +/** + * Obtain the lock on the given `PARCDeque` instance. + * + * If the lock is already held by another thread, this function will block. + * If the lock is aleady held by the current thread, this function will return `false`. + * + * Implementors must avoid deadlock by attempting to lock the object a second time within the same calling thread. + * + * @param [in] object A pointer to a valid `PARCDeque` instance. + * + * @return true The lock was obtained successfully. + * @return false The lock is already held by the current thread, or the `PARCDeque` is invalid. + * + * Example: + * @code + * { + * if (parcDeque_Lock(object)) { + * + * } + * } + * @endcode + */ +parcObject_ImplementLock(parcDeque, PARCDeque); + +/** + * Try to obtain the advisory lock on the given PARCDeque instance. + * + * Once the lock is obtained, the caller must release the lock as soon as possible. + * + * @param [in] object A pointer to a valid PARCDeque instance. + * + * @return true The PARCDeque is locked. + * @return false The PARCDeque is unlocked. + * + * Example: + * @code + * { + * parcDeque_TryLock(object); + * } + * @endcode + */ +parcObject_ImplementTryLock(parcDeque, PARCDeque); + +/** + * Try to unlock the advisory lock on the given `PARCDeque` instance. + * + * @param [in] object A pointer to a valid `PARCDeque` instance. + * + * @return true The `PARCDeque` was locked and now is unlocked. + * @return false The `PARCDeque` was not locked and remains unlocked. + * + * Example: + * @code + * { + * parcDeque_Unlock(object); + * } + * @endcode + */ +parcObject_ImplementUnlock(parcDeque, PARCDeque); + +/** + * Determine if the advisory lock on the given `PARCDeque` instance is locked. + * + * @param [in] object A pointer to a valid `PARCDeque` instance. + * + * @return true The `PARCDeque` is locked. + * @return false The `PARCDeque` is unlocked. + * Example: + * @code + * { + * if (parcDeque_IsLocked(object)) { + * ... + * } + * } + * @endcode + */ +parcObject_ImplementIsLocked(parcDeque, PARCDeque); + +#endif // libparc_parc_Deque_h diff --git a/libparc/parc/algol/parc_Dictionary.c b/libparc/parc/algol/parc_Dictionary.c new file mode 100755 index 00000000..628b08e2 --- /dev/null +++ b/libparc/parc/algol/parc_Dictionary.c @@ -0,0 +1,134 @@ +/* + * 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 <LongBow/runtime.h> + +#include <string.h> + +#include <parc/algol/parc_Dictionary.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_TreeRedBlack.h> + +struct parc_dictionary { + PARCDictionary_CompareKey keyCompareFunction; + PARCDictionary_KeyHashFunc keyHashFunction; + PARCDictionary_FreeKey keyFreeFunction; + PARCDictionary_FreeValue valueFreeFunction; + PARCDictionary_ValueEquals valueEqualsFunction; + PARCTreeRedBlack *tree; +}; + + +PARCDictionary * +parcDictionary_Create(PARCDictionary_CompareKey keyCompareFunction, + PARCDictionary_KeyHashFunc keyHashFunction, + PARCDictionary_FreeKey keyFreeFunction, + PARCDictionary_ValueEquals valueEqualsFunction, + PARCDictionary_FreeValue valueFreeFunction) +{ + assertNotNull(keyCompareFunction, "KeyCompareFunction can't be null"); + assertNotNull(keyHashFunction, "KeyHashFunction can't be null"); + PARCDictionary *dictionary = parcMemory_Allocate(sizeof(PARCDictionary)); + assertNotNull(dictionary, "parcMemory_Allocate(%zu) returned NULL", sizeof(PARCDictionary)); + dictionary->keyCompareFunction = keyCompareFunction; + dictionary->keyHashFunction = keyHashFunction; + dictionary->keyFreeFunction = keyFreeFunction; + dictionary->valueFreeFunction = valueFreeFunction; + dictionary->valueEqualsFunction = valueEqualsFunction; + dictionary->tree = parcTreeRedBlack_Create(keyCompareFunction, + keyFreeFunction, + NULL, + valueEqualsFunction, + valueFreeFunction, + NULL); + return dictionary; +} + + +void +parcDictionary_Destroy(PARCDictionary **dictionaryPointer) +{ + assertNotNull(dictionaryPointer, "Pointer to dictionary pointer can't be NULL"); + assertNotNull(*dictionaryPointer, "Pointer to dictionary can't be NULL"); + parcTreeRedBlack_Destroy(&((*dictionaryPointer)->tree)); + parcMemory_Deallocate((void **) dictionaryPointer); + *dictionaryPointer = NULL; +} + +void +parcDictionary_SetValue(PARCDictionary *dictionary, void *key, void *value) +{ + assertNotNull(dictionary, "dictionary pointer can't be NULL"); + assertNotNull(key, "Key pointer can't be NULL"); + parcTreeRedBlack_Insert(dictionary->tree, key, value); +} + +void * +parcDictionary_GetValue(PARCDictionary *dictionary, const void *key) +{ + assertNotNull(dictionary, "dictionary pointer can't be NULL"); + assertNotNull(key, "Key pointer can't be NULL"); + return parcTreeRedBlack_Get(dictionary->tree, key); +} + +void * +parcDictionary_RemoveValue(PARCDictionary *dictionary, const void *key) +{ + assertNotNull(dictionary, "dictionary pointer can't be NULL"); + assertNotNull(key, "Key pointer can't be NULL"); + return parcTreeRedBlack_Remove(dictionary->tree, key); +} + +void +parcDictionary_RemoveAndDestroyValue(PARCDictionary *dictionary, const void *key) +{ + assertNotNull(dictionary, "dictionary pointer can't be NULL"); + assertNotNull(key, "Key pointer can't be NULL"); + parcTreeRedBlack_RemoveAndDestroy(dictionary->tree, key); +} + +PARCArrayList * +parcDictionary_Keys(const PARCDictionary *dictionary) +{ + assertNotNull(dictionary, "dictionary pointer can't be NULL"); + return parcTreeRedBlack_Keys(dictionary->tree); +} + +PARCArrayList * +parcDictionary_Values(const PARCDictionary *dictionary) +{ + assertNotNull(dictionary, "dictionary pointer can't be NULL"); + return parcTreeRedBlack_Values(dictionary->tree); +} + +size_t +parcDictionary_Size(const PARCDictionary *dictionary) +{ + assertNotNull(dictionary, "dictionary pointer can't be NULL"); + return parcTreeRedBlack_Size(dictionary->tree); +} + +int +parcDictionary_Equals(const PARCDictionary *dictionary1, const PARCDictionary *dictionary2) +{ + assertNotNull(dictionary1, "dictionary pointer can't be NULL"); + assertNotNull(dictionary2, "dictionary pointer can't be NULL"); + return parcTreeRedBlack_Equals(dictionary1->tree, dictionary2->tree); +} diff --git a/libparc/parc/algol/parc_Dictionary.h b/libparc/parc/algol/parc_Dictionary.h new file mode 100755 index 00000000..1ba98a65 --- /dev/null +++ b/libparc/parc/algol/parc_Dictionary.h @@ -0,0 +1,294 @@ +/* + * 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. + */ + +/** + * @file parc_Dictionary.h + * @ingroup datastructures + * @brief + * + * The `PARCDictionary` is a dictionary of key value tuples. + * + */ +#ifndef libparc_parc_Dictionary_h +#define libparc_parc_Dictionary_h + +#include <parc/algol/parc_ArrayList.h> + +#include <stdint.h> + +struct parc_dictionary; + + +/** + * @typedef PARCDictionary + * @brief A PARCDictionary is a dictionary of key-values + */ +typedef struct parc_dictionary PARCDictionary; + +/** + * @typedef `PARCDictionary_CompareKey` + * + * @brief Compare two dictionary keys + * + * @param [in] key1 First dictionary key + * @param [in] key2 Second dictionary key + * + * Example: + * @code + * <#example#> + * @endcode + */ +typedef int (*PARCDictionary_CompareKey)(const void *key1, + const void *key2); +/** + * @typedef `PARCDictionary_ValueEquals` + * + * @brief Compare two values + * + * @param [in] value1 A pointer to the First value + * @param [in] value2 A pointer to the Second value + * + * Example: + * @code + * <#example#> + * @endcode + */ +typedef bool (*PARCDictionary_ValueEquals)(const void *value1, const void *value2); + + +/** + * @typedef `PARCDictionary_KeyHashFunc` + * @brief The key hash function must return a 32 bit hash of the key + * @param [in] key pointer to the key to be hashed + * @return uint32 hash + */ + +typedef uint32_t (*PARCDictionary_KeyHashFunc)(const void *key); + +/** + * @typedef `PARCDictionary_FreeValue` + * @brief The free value must free a value. + * This will be called when a value for a key is changed. + * It will also be used when the Dictionary is destroyed on any + * values in the dictionary. + * @param [in,out] value The pointer to the pointer of the value to be freed. + */ + +typedef void (*PARCDictionary_FreeValue)(void **value); + +/** + * @typedef `PARCDictionary_FreeKey` + * @brief The free key must free a key. + * This function will be called when the value for a key is removed from the + * dictionary. It's also called when the dictionary is destroyed. + * @param [in,out] key The pointer to the pointer to the key to be freed. + */ + +typedef void (*PARCDictionary_FreeKey)(void **key); + +/** + * Create a Dictionary. + * You MUST set the function to compare keys and hash keys. + * You can give NULL as the free function of the key and the data, + * but why would you do that? :-) + * + * @param [in] keyCompareFunction The function that compares 2 keys (can't be NULL) + * @param [in] keyHashFunction The function to hash the keys to 32 bit values (can't be NULL) + * @param [in] keyFreeFunction The function to free the key (can be NULL) + * @param [in] valueEqualsFunction The function to know that values are equal. If NULL then values won't be compared on equality. + * @param [in] valueFreeFunction The function to free the values (can be NULL) + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCDictionary *parcDictionary_Create(PARCDictionary_CompareKey keyCompareFunction, + PARCDictionary_KeyHashFunc keyHashFunction, + PARCDictionary_FreeKey keyFreeFunction, + PARCDictionary_ValueEquals valueEqualsFunction, + PARCDictionary_FreeValue valueFreeFunction); + +/** + * Destroy a Dictionary. If the Free functions were passed to the constructor and are not NULL + * they will be called for every element. + * + * @param [in,out] dictionaryPointer A pointer to the pointer to the instance of `PARCDictionary` to be destroyed + * + * Example: + * @code + * <#example#> + * @endcode + */ +void parcDictionary_Destroy(PARCDictionary **dictionaryPointer); + +/** + * Set a value for a key. + * Both key and value will be referenced by the Dictionary. No memory copies will happen. + * + * If the key does not exist a new dictionary entry will be added pointing to the value. + * + * If the key does exists then the old value will be freed using the valueFree function (if not NULL). + * The old key will also be freed using the keyFree function (if not NULL). + * + * @param [in,out] dictionary A pointer to the specified `PARCDictionary` + * @param [in] key The key to insert in the dictionary. Can't be NULL. + * @param [in] value The value to associate with that key. Can't be NULL. + * + * Example: + * @code + * <#example#> + * @endcode + */ + +void parcDictionary_SetValue(PARCDictionary *dictionary, void *key, void *value); + +/** + * Get a value from the dictionary associated with a specific key. + * + * @param [in] dictionary A PARCDictionary + * @param [in] key A pointer to the key. It must be valid as a parameter to the key compare function of the + * dictionary + * @return A pointer to the value. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void *parcDictionary_GetValue(PARCDictionary *dictionary, const void *key); + +/** + * Remove a value from the dictionary. This will return the value referenced by the key. It will remove it + * in the process. The key will be freed with the keyFree function (if not NULL) + * + * @param [in,out] dictionary A `PARCDictionary` + * @param [in] key A pointer to the key. It must be valid as a parameter to the key compare function of the dictionary. + * @return A pointer to the value. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void *parcDictionary_RemoveValue(PARCDictionary *dictionary, const void *key); + +/** + * Delete an entry from the dictionary. + * This will remove an entry from the dictionary and free both the key and the value using the functions + * provided at the creation of the dictionary. + * + * @param [in,out] dictionary A pointer to an instance of `PARCDictionary` + * @param [in] key A pointer to the key. It must be valid as a parameter to the key compare function of the dictionary. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void parcDictionary_RemoveAndDestroyValue(PARCDictionary *dictionary, const void *key); + +/** + * Get a list of the keys from this dictionary. + * + * @param [in] dictionary A `PARCDictionary` + * @return A pointer to a {@link PARCArrayList} of (pointers to) keys. All of these keys will be valid keys in the dictionary. + * The caller will own the list of keys and should destroy it when done. The caller will not own + * the key themselves. (Destroying the `PARCArrayList` should be enough). + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCArrayList *parcDictionary_Keys(const PARCDictionary *dictionary); + +/** + * Get a list of the values from this dictionary. + * The caller will own the list of values and should destroy it when done. The caller will not own + * the values themselves. Destroying the {@link PARCArrayList} should be enough. + * + * Note that if the Dictionary is destroyed the value pointers might no longer point to valid values. + * + * @param [in] dictionary A pointer to an instance of `PARCDictionary` + * @return A pointer to a `PARCArrayList` of (pointers to) values. + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCArrayList *parcDictionary_Values(const PARCDictionary *dictionary); + +/** + * Return the number of entries in the dictionary. + * + * @param [in] dictionary A pointer to an instance of `PARCDictionary` + * @return The number of keys in the dictionary. + * + * Example: + * @code + * <#example#> + * @endcode + */ +size_t parcDictionary_Size(const PARCDictionary *dictionary); + + +/** + * Determine if two `PARCDictionary` instances are equal. + * + * Two `PARCDictionary` instances are equal if, and only if, the trees are equal + * + * The following equivalence relations on non-null `PARCDictionary` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, `PARCDictionary_Equals(x, x)` + * must return true. + * + * * It is symmetric: for any non-null reference values x and y, + * `parcDictionary_Equals(x, y)` must return true if and only if + * `parcDictionary_Equals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcDictionary_Equals(x, y)` returns true and + * `parcDictionary_Equals(y, z)` returns true, + * then `parcDictionary_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple + * invocations of `parcDictionary_Equals(x, y)` consistently return true or + * consistently return false. + * + * * For any non-null reference value x, `parcDictionary_Equals(x, NULL)` must + * return false. + * + * @param [in] dictionary1 A pointer to a `PARCDictionary` instance. + * @param [in] dictionary2 A pointer to a `PARCDictionary` instance. + * @return true if the two `PARCDictionary` instances are equal. + * + * Example: + * @code + * { + * PARCDictionary *a = parcDictionary_Create(); + * PARCDictionary *b = parcDictionary_Create(); + * + * if (parcDictionary_Equals(a, b)) { + * // true + * } else { + * // false + * } + * } + * @endcode + */ +int parcDictionary_Equals(const PARCDictionary *dictionary1, const PARCDictionary *dictionary2); +#endif // libparc_parc_Dictionary_h diff --git a/libparc/parc/algol/parc_DisplayIndented.c b/libparc/parc/algol/parc_DisplayIndented.c new file mode 100755 index 00000000..5e043c15 --- /dev/null +++ b/libparc/parc/algol/parc_DisplayIndented.c @@ -0,0 +1,114 @@ +/* + * 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 <LongBow/runtime.h> + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> + +#include <parc/algol/parc_DisplayIndented.h> + +static char *_spaces = " "; + +static size_t _indentationFactor = 2; + +static size_t +_indent(int indentation) +{ + size_t result = 0; + + if (indentation > 0) { + result = write(1, _spaces, indentation * _indentationFactor); + assertTrue(result == (indentation * _indentationFactor), + "Write(2) failed to write %zd bytes.", indentation * _indentationFactor); + } + return result; +} + +static void +_parcDisplayIndented_Print(int indentation, char *string) +{ + char *start = string; + char *end = strchr(start, '\n'); + + while (start != NULL) { + _indent(indentation); + if (end != NULL) { + ssize_t nwritten = write(1, start, end - start + 1); + assertTrue(nwritten >= 0, "Error calling write"); + start = end + 1; + } else { + ssize_t nwritten = write(1, start, strlen(start)); + assertTrue(nwritten >= 0, "Error calling write"); + break; + } + end = strchr(start, '\n'); + } +} + +void +parcDisplayIndented_PrintLine(int indentation, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + + char *cString; + int length = vasprintf(&cString, format, ap); + assertTrue(length >= 0, "Error in vasprintf"); + + va_end(ap); + + _parcDisplayIndented_Print(indentation, cString); + + ssize_t nwritten = write(1, "\n", 1); + assertTrue(nwritten >= 0, "Error calling write"); + + free(cString); +} + +void +parcDisplayIndented_PrintMemory(int indentation, size_t length, const char memory[length]) +{ + int bytesPerLine = 16; + + char accumulator[bytesPerLine + 1]; + memset(accumulator, ' ', bytesPerLine); + accumulator[bytesPerLine] = 0; + + char *cString; + for (size_t offset = 0; offset < length; /**/) { + int nwritten = asprintf(&cString, "%p=[", &memory[offset]); + assertTrue(nwritten >= 0, "Error calling asprintf"); + _parcDisplayIndented_Print(indentation, cString); + free(cString); + + size_t bytesInLine = (length - offset) < bytesPerLine ? (length - offset) : bytesPerLine; + for (size_t i = 0; i < bytesInLine; i++) { + char c = memory[offset + i]; + printf("0x%02x, ", c & 0xFF); + accumulator[i] = isprint(c) ? c : '.'; + } + offset += bytesInLine; + } + printf(" %s]\n", accumulator); +} diff --git a/libparc/parc/algol/parc_DisplayIndented.h b/libparc/parc/algol/parc_DisplayIndented.h new file mode 100644 index 00000000..753f942d --- /dev/null +++ b/libparc/parc/algol/parc_DisplayIndented.h @@ -0,0 +1,62 @@ +/* + * 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. + */ + +/** + * @file parc_DisplayIndented.h + * @ingroup developer + * @brief Support for displaying information on the console. + * + * + */ +#ifndef libparc_parc_DisplayIndented_h +#define libparc_parc_DisplayIndented_h + +#include <stdarg.h> +#include <stdlib.h> + +/** + * Print an indented, formatted string on standard output. + * + * The line is automatically terminated with a new line. + * + * @param [in] indentation The indentation level of the output. + * @param [in] format The format string. + * @param [in] ... A variable number of arguments. + * + * Example: + * @code + * { + * parcDisplayIndented_PrintLine(2, "This is printed on standard output, at indentation level 2"); + * } + * @endcode + */ +void parcDisplayIndented_PrintLine(int indentation, const char *format, ...); + +/** + * Print memory. + * + * @param [in] indentation The indentation level of the output. + * @param [in] length The length of the array. + * @param [in] memory The memory array. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +void parcDisplayIndented_PrintMemory(int indentation, size_t length, const char *memory); +#endif // libparc_parc_DisplayIndented_h diff --git a/libparc/parc/algol/parc_ElasticString.h b/libparc/parc/algol/parc_ElasticString.h new file mode 100755 index 00000000..c6e39eb1 --- /dev/null +++ b/libparc/parc/algol/parc_ElasticString.h @@ -0,0 +1,439 @@ +/* + * 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. + */ + +/** + * @file parc_ElasticString.h + * @ingroup memory + * @brief An elastic C string. + * + * An elastic string is a dynamic array of characters which are readily expressed as a + * nul-terminated C string. + * + */ +#ifndef libparc_parc_ElasticString_h +#define libparc_parc_ElasticString_h + +#include <stdarg.h> + +#include <parc/algol/parc_Buffer.h> + +struct parc_elastic_string; +typedef struct parc_elastic_string PARCElasticString; + +/** + * Perform validation on a pointer to a `PARCElasticString`. + * + * If invalid, this function will abort the running process. + * + * @param * string A pointer to a `PARCElasticString` to validate. + * + * Example: + * @code + * { + * PARElasticString *string = parcElasticString_Create(); + * + * parcElasticString_AssertValid(string); + * } + * @endcode + */ +void parcElasticString_AssertValid(const PARCElasticString *string); + +/** + * Create an empty `PARCElasticString` instance. + * + * The instance will be empty upon initialization (i.e., `parcElasticString_ToString()` will + * return an empty string), but characters and strings may be inserted/appended to + * the instance to produce usable content, + * + * @return A pointer to an allocated `PARCElasticString` that must be freed with `parcElasticString_Release()`. + * + * Example: + * @code + * { + * PARElasticString *string = parcElasticString_Create(); + * + * // use the string as necessary + * + * parcElasticString_Release(&string); + * } + * @endcode + */ +PARCElasticString *parcElasticString_Create(void); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param * string A pointer to a pointer to the instance to release. + * + * @return The number of remaining references to the object. + * + * Example: + * @code + * { + * PARCElasticString *buffer = parcElasticString_Create(); + * + * parcElasticString_Release(&pathName); + * } + * @endcode + */ +void parcElasticString_Release(PARCElasticString **string); + +/** + * Retrieve the number of remaining bytes between the current position + * in the string and its (flexible) limit. + * + * @param * string A pointer to an `PARCElasticString` instance. + * + * @return The non-negative number of characters remaining between the position and limit. + * + * Example: + * @code + * { + * char *inputString = "Hello World"; + * size_t inputLength = strlen(input); + * PARElasticString *string = parcElasticString_Create(); + * parcElasticString_PutString(string, inputString); + * parcElasticString_Flip(string); + * size_t numRemaining = parcElasticString_Remaining(string); + * + * // numRemaining == inputLength + * + * parcElasticString_Release(&string); + * } + * @endcode + */ +size_t parcElasticString_Remaining(const PARCElasticString *string); + +/** + * Set the limit to the current position, then the position to zero. + * If the mark is defined, it is invalidated, + * and any subsequent operation that requires the mark will abort until the mark + * is set again via `parcElasticString_Mark`. + * + * @param * string A pointer to an `PARCElasticString` instance. + * + * @return The same pointer as the `string` parameter. + * + * Example: + * @code + * { + * char *inputString = "Hello World"; + * size_t inputLength = strlen(input); + * PARElasticString *string = parcElasticString_Create(); + * parcElasticString_PutString(string, inputString); + * parcElasticString_Flip(string); + * size_t numRemaining = parcElasticString_Remaining(string); + * + * // numRemaining == inputLength + * + * parcElasticString_Release(&string); + * } + * @endcode + */ +PARCElasticString *parcElasticString_Flip(PARCElasticString *string); + +/** + * Return the given `PARCElasticString`'s position. + * + * A buffer's position is the index of the next element to be read or written. + * A buffer's position is never negative and is never greater than its limit. + * + * @param * string A pointer to a `PARCElasticString` instance. + * + * @return The given `PARCElasticString`'s position. + * + * Example: + * @code + * { + * size_t currentPosition = parcBuffer_Position(buffer); + * } + * @endcode + * + * @see parcElasticString_SetPosition + */ +size_t parcElasticString_Position(const PARCElasticString *string); + +/** + * Set the position in the given `PARCElasticString`. + * + * If the mark is defined and larger than the new position then it is invalidated. + * + * @param * string A pointer to a `PARCElasticString` instance. + * @param * newPosition The new position for the `PARCElasticString`. + * + * @return The same pointer as the `string` parameter. + * + * Example: + * @code + * { + * PARElasticString *string = parcElasticString_Create(); + * parcElasticString_PutString(string, "Hello World"); + * + * parcElasticString_SetPosition(string, 0); + * + * // position is now at 0, instead of at the end of "Hello World" + * + * parcElasticString_Release(&string); + * } + * @endcode + * + * @see parcElasticString_Position + */ +PARCElasticString *parcElasticString_SetPosition(PARCElasticString *string, size_t newPosition); + +/** + * Append an array with the specified number of bytes to the end of this `PARCElasticString` instance. + * + * The position of the string is advanced by the length of the array. + * + * @param * string A pointer to a `PARCElasticString` instance. + * @param * array A pointer to the array containing the bytes to append. + * @param * length The length of the input array. + * + * @return The same pointer as the `string` parameter. + * + * Example: + * @code + * { + * PARElasticString *string = parcElasticString_Create(); + * + * uint8_t * appendArray = { 0x00, 0x01, 0x02, 0x03, 0x04 }; + * parcElasticString_PutArray(string, appendArray, 5); + * + * parcElasticString_Release(&string); + * } + * @endcode + * + * @see parcElasticString_PutString + */ +PARCElasticString *parcElasticString_PutArray(PARCElasticString *string, const char *array, size_t length); + +/** + * Append a C-string to the end of this `PARCElasticString` instance. + * + * The position of the string is advanced by the length of the string. + * + * @param * string A pointer to a `PARCElasticString` instance. + * @param * cString A pointer to a nul-terminated C string to append to this `PARCElasticString`. + * + * @return The same pointer as the `string` parameter. + * + * Example: + * @code + * { + * PARElasticString *string = parcElasticString_Create(); + * + * parcElasticString_PutString(string, "Hello World"); + * printf("String = %s\n", parcElasticString_ToString(string)); + * + * parcElasticString_Release(&string); + * } + * @endcode + * + * @see parcElasticString_PutArray + */ +PARCElasticString *parcElasticString_PutString(PARCElasticString *string, const char *cString); + +/** + * Append the contents of a `PARCBuffer` instance to the end of the `PARCElasticString` instance. + * + * The position of the string is advanced by the length of the buffer. + * + * @param * string A pointer to a `PARCElasticString` instance. + * @param * buffer A pointer to a `PARCBuffer` instance. + * + * @return The same pointer as the `string` parameter. + * + * Example: + * @code + * { + * PARElasticString *string = parcElasticString_Create(); + * + * uint8_t * array = { 'H', 'e', 'l', 'l', 'o' }; + * PARCBuffer *endBuffer = parcBuffer_Allocate(10); + * parcBuffer_PutArray(endBuffer, 5, array); + * parcElasticString_PutBuffer(string, endBuffer); + * + * printf("String = %s\n", parcElasticString_ToString(string)); + * + * parcElasticString_Release(&string); + * } + * @endcode + * + * @see parcElasticString_PutString + */ +PARCElasticString *parcElasticString_PutBuffer(PARCElasticString *string, PARCBuffer *buffer); + +/** + * Append a single character (byte) to the end of this string. + * + * The position of the string is advanced by one (1). + * + * @param * string A pointer to `PARCElasticString` + * @param * character A `char` value to append. + * + * @return The same pointer as the `string` parameter. + * + * Example: + * @code + * { + * PARElasticString *string = parcElasticString_Create(); + * + * parcElasticString_PutChar(string, 'H'); + * parcElasticString_PutChar(string, 'e'); + * parcElasticString_PutChar(string, 'l'); + * parcElasticString_PutChar(string, 'l'); + * parcElasticString_PutChar(string, 'o'); + * + * printf("String = %s\n", parcElasticString_ToString(string)); + * + * parcElasticString_Release(&string); + * } + * @endcode + * + * @see parcElasticString_PutString + */ +PARCElasticString *parcElasticString_PutChar(PARCElasticString *string, const char character); + +/** + * Put a variable number of characters into the `PARCElasticString`. + * + * @param * string The `PARCElasticString` to receive the characters. + * @param * count The number of characters to insert into the `PARCElasticString` + * @param * ... The characters to insert into the `PARCElasticString`. + * + * @return The same pointer as the `string` parameter. + * + * Example: + * @code + * { + * PARElasticString *string = parcElasticString_Create(); + * + * parcElasticString_PutChar(string, 5, 'H', 'e', 'l', 'l', 'o'); + * + * printf("String = %s\n", parcElasticString_ToString(string)); + * + * parcElasticString_Release(&string); + * } + * @endcode + * + * @see parcElasticString_PutString + */ +PARCElasticString *parcElasticString_PutChars(PARCElasticString *string, unsigned int count, ...); + +/** + * Append an arbitrary number of C-style strings to the given `PARCElasticString` instance. + * + * @param * string A pointer to `PARCElasticString` + * @param * ... The nul-terminated, C-style strings to append to the given `PARCElasticString`. + * + * @return The same pointer as the `string` parameter. + * + * Example: + * @code + * { + * PARElasticString *baseString = parcElasticString_Create(); + * parcElasticString_PutString(baseString, "Hello"); + * uint8_t * string1 = {' ', '\0'}; + * uint8_t * string2 = {'W', 'o', 'r', 'l', 'd', '\0'}; + * + * parcElasticString_PutStrings(baseString, string1, string2); + * + * printf("String = %s\n", parcElasticString_ToString(baseString)); + * + * parcElasticString_Release(&baseString); + * } + * @endcode + */ +PARCElasticString *parcElasticString_PutStrings(PARCElasticString *string, ...); + +/** + * Append a formatted nul-terminated, C-style string string to the given `PARCElasticString` instance. + * + * @param * string A pointer to `PARCElasticString`. + * @param * format The format string + * @param * ... Remaining parameters used to format the string. + * + * @return The same pointer as the `string` parameter. + * + * Example: + * @code + * { + * PARElasticString *string = parcElasticString_Create(); + * + * parcElasticString_Format(string, "Hello %s\n", "World"); + * + * printf("String = %s\n", parcElasticString_ToString(string)); + * + * parcElasticString_Release(&string); + * } + * @endcode + */ +PARCElasticString *parcElasticString_Format(PARCElasticString *string, const char *format, ...) \ + __attribute__((format(printf, 2, 3))); + +/** + * Retrieve a handle to the `PARCBuffer` instance. The reference count is not increased. + * + * @param * string A pointer to `PARCElasticString`. + * + * @return The `PARCBuffer` instance used to encapsulate the `PARCElasticString` contents. + * + * Example: + * @code + * { + * PARElasticString *string = parcElasticString_Create(); + * parcElasticString_PutString(string, "Hello World"); + * + * PARCBuffer *buffer = parcElasticString_ToBuffer(string); + * printf("String in hex = %s\n", parcBuffer_ToHexString(buffer)); + * + * parcElasticString_Release(&string); + * } + * @endcode + */ +PARCBuffer *parcElasticString_ToBuffer(const PARCElasticString *string); + +/** + * Produce a C string representation of the given `PARCElasticString`. + * + * Produce an allocated, nul-terminated string representation of the given `PARCElasticString`. + * The result must be freed by the caller via the `parcMemory_Deallocate()` function. + * + * @param * string A pointer to `PARCElasticString`. + * + * @return A pointer to an allocated array containing a nul-terminated string the must be freed via `parcMemory_Deallocate()`. + * + * Example: + * @code + * { + * PARElasticString *string = parcElasticString_Create(); + * parcElasticString_PutString(string, "Hello World"); + * + * printf("String = %s\n", parcElasticString_ToString(string)); + * + * parcElasticString_Release(&string); + * } + * @endcode + */ +char *parcElasticString_ToString(const PARCElasticString *string); +#endif // libparc_parc_ElasticString_h diff --git a/libparc/parc/algol/parc_Environment.c b/libparc/parc/algol/parc_Environment.c new file mode 100644 index 00000000..2955a139 --- /dev/null +++ b/libparc/parc/algol/parc_Environment.c @@ -0,0 +1,45 @@ +/* + * 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 <sys/types.h> +#include <pwd.h> +#include <stdlib.h> +#include <unistd.h> + +#include <parc/algol/parc_Environment.h> +#include <parc/algol/parc_File.h> + +const char * +parcEnvironment_GetHomeDirectory(void) +{ + char *result = getenv("HOME"); + return result; +} + +PARCFile * +parcEnvironment_HomeDirectory(void) +{ + char *path; + + if ((path = getenv("HOME")) == NULL) { + path = getpwuid(getuid())->pw_dir; + } + + return parcFile_Create(path); +} diff --git a/libparc/parc/algol/parc_Environment.h b/libparc/parc/algol/parc_Environment.h new file mode 100755 index 00000000..04610176 --- /dev/null +++ b/libparc/parc/algol/parc_Environment.h @@ -0,0 +1,42 @@ +/* + * 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. + */ + +/** + * @file parc_Environment.h + * @ingroup inputoutput + * @brief Functions to access and manipulate the runtime environment. + * + * + * Example: + * @code + * <#example#> + * @endcode + */ +#ifndef libparc_parc_Environment_h +#define libparc_parc_Environment_h + +/** + * + * Get the current home directory for the running process. + * + * @return A C string containing the name of the home directory. + * + * Example: + * @code + * <#example#> + * @endcode + */ +const char *parcEnvironment_GetHomeDirectory(void); +#endif // libparc_parc_Environment_h diff --git a/libparc/parc/algol/parc_Event.c b/libparc/parc/algol/parc_Event.c new file mode 100755 index 00000000..0a8391aa --- /dev/null +++ b/libparc/parc/algol/parc_Event.c @@ -0,0 +1,149 @@ +/* + * 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 <LongBow/runtime.h> + +#include "internal_parc_Event.h" +#include <parc/algol/parc_EventScheduler.h> +#include <parc/algol/parc_Event.h> +#include <parc/algol/parc_FileOutputStream.h> +#include <parc/logging/parc_Log.h> +#include <parc/logging/parc_LogReporterFile.h> + +static int _parc_event_debug_enabled = 0; + +#define parcEvent_LogDebug(parcEvent, ...) \ + if (_parc_event_debug_enabled) \ + parcLog_Debug(parcEventScheduler_GetLogger(parcEvent->parcEventScheduler), __VA_ARGS__) + +/** + * Current implementation based on top of libevent2 + */ +#include <event2/event.h> + +/** + * @typedef PARCEvent + * @brief A structure containing private event state data variables + */ +struct PARCEvent { + /** + * The event instance. + */ + struct event *event; + + // Event scheduler we have been queued with + PARCEventScheduler *parcEventScheduler; + + // Interpose on callback + PARCEvent_Callback *callback; + void *callbackUserData; + + void *user_data; +}; + +static void +_parc_event_callback(evutil_socket_t fd, short flags, void *context) +{ + PARCEvent *parcEvent = (PARCEvent *) context; + parcEvent_LogDebug(parcEvent, "_parc_event_callback(fd=%x,flags=%x,parcEvent=%p)\n", fd, flags, parcEvent); + + parcEvent->callback((int) fd, internal_libevent_type_to_PARCEventType(flags), parcEvent->callbackUserData); +} + +PARCEvent * +parcEvent_Create(PARCEventScheduler *parcEventScheduler, int fd, PARCEventType flags, PARCEvent_Callback *callback, void *callbackArgs) +{ + PARCEvent *parcEvent = parcMemory_Allocate(sizeof(PARCEvent)); + assertNotNull(parcEvent, "parcMemory_Allocate(%zu) returned NULL", sizeof(PARCEvent)); + + parcEvent->parcEventScheduler = parcEventScheduler; + parcEvent->callback = callback; + parcEvent->callbackUserData = callbackArgs; + + parcEvent->event = event_new(parcEventScheduler_GetEvBase(parcEventScheduler), fd, + internal_PARCEventType_to_libevent_type(flags), _parc_event_callback, parcEvent); + assertNotNull(parcEvent->event, "Could not create a new event!"); + + parcEvent_LogDebug(parcEvent, + "parcEvent_Create(base=%p,fd=%x,events=%x,cb=%p,args=%p)\n", + parcEventScheduler_GetEvBase(parcEventScheduler), fd, flags, callback, parcEvent); + + return parcEvent; +} + +int +parcEvent_Start(PARCEvent *parcEvent) +{ + parcEvent_LogDebug(parcEvent, "parcEvent_Start(%p)\n", parcEvent); + assertNotNull(parcEvent, "parcEvent_Start must be passed a valid event!"); + + int result = event_add(parcEvent->event, NULL); + return result; +} + +int +parcEvent_Stop(PARCEvent *parcEvent) +{ + parcEvent_LogDebug(parcEvent, "parcEvent_Stop(%p)\n", parcEvent); + assertNotNull(parcEvent, "parcEvent_Stop must be passed a valid event!"); + + int result = event_del(parcEvent->event); + return result; +} + +int +parcEvent_Poll(PARCEvent *parcEvent, PARCEventType event) +{ + parcEvent_LogDebug(parcEvent, "parcEvent_Stop(%p)\n", parcEvent); + assertNotNull(parcEvent, "parcEvent_Stop must be passed a valid event!"); + + int result = event_pending(parcEvent->event, event, NULL); + return result; +} + +void +parcEvent_Destroy(PARCEvent **parcEvent) +{ + parcEvent_LogDebug((*parcEvent), "parcEvent_Destroy(%p)\n", *parcEvent); + assertNotNull(*parcEvent, "parcEvent_Destroy must be passed a valid parcEvent!"); + assertNotNull((*parcEvent)->event, "parcEvent_Destroy passed a null event!"); + + event_free((*parcEvent)->event); + parcMemory_Deallocate((void **) parcEvent); +} + +int +parcEvent_SetPriority(PARCEvent *parcEvent, PARCEventPriority priority) +{ + parcEvent_LogDebug(parcEvent, "parcEvent_Stop(%p)\n", parcEvent); + + return event_priority_set(parcEvent->event, internal_PARCEventPriority_to_libevent_priority(priority)); +} + +void +parcEvent_EnableDebug(void) +{ + _parc_event_debug_enabled = 1; +} + +void +parcEvent_DisableDebug(void) +{ + _parc_event_debug_enabled = 0; +} diff --git a/libparc/parc/algol/parc_Event.h b/libparc/parc/algol/parc_Event.h new file mode 100644 index 00000000..3f2fde98 --- /dev/null +++ b/libparc/parc/algol/parc_Event.h @@ -0,0 +1,211 @@ +/* + * 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. + */ + +/** + * @file parc_Event.h + * @ingroup events + * @brief Event management + * + * Provides a facade implementing many regularly available event functions. + * This is an interface that software implementors may use to substitute + * different kinds of underlying implementations of these event management functions. + * Notable examples are libevent and libev. + * + */ +#ifndef libparc_parc_Event_h +#define libparc_parc_Event_h + +/** + * Current implementation based on top of libevent2 + */ + +#include <parc/algol/parc_EventScheduler.h> + +/** + * @typedef PARCEventType + * @brief An enumeration of event types, with an additional persist flag + */ +typedef enum { + PARCEventType_None = 0x00, + PARCEventType_Timeout = 0x01, + PARCEventType_Read = 0x02, + PARCEventType_Write = 0x04, + PARCEventType_Signal = 0x08, + PARCEventType_Persist = 0x10, + PARCEventType_EdgeTriggered = 0x20 +} PARCEventType; + +/** + * @typedef PARCEventPriority + * @brief Priority flags for queue scheduling, these currently match the RTA_*_PRIORITY + * this will eventually be replaced. + */ +typedef enum { + PARCEventPriority_Maximum = 0, + PARCEventPriority_Normal = 1, + PARCEventPriority_Minimum = 2, + PARCEventPriority_NumberOfPriorities = 3 +} PARCEventPriority; + +/** + * @typedef PARCEvent_Callback + * @brief Event callback definition + */ +typedef void (PARCEvent_Callback)(int fileDescriptor, PARCEventType type, void *user_data); + +/** + * @typedef PARCEvent + * @brief A structure containing private libevent state data variables + */ +typedef struct PARCEvent PARCEvent; + +/** + * Create a new PARCEvent instance. + * + * A new PARCEvent instance is returned. + * + * @param [in] parcEventScheduler base instance + * @param [in] fileDescriptor file descriptor to monitor + * @param [in] events to catch + * @param [in] callback function + * @param [in] callbackArgs function private arguments + * @returns A pointer to the a PARCEvent instance. + * + * Example: + * @code + * static void + * _read_callback(int fileDescriptor, PARCEventType type, void *args) + * { + * } + * + * { + * PARCEventScheduler *eventScheduler = parcEventScheduer_Create(); + * PARCEvent *event = parcEvent_Create(eventScheduler, fileDescriptor, PARCEvent_ReadEvent, _read_callback, _read_callback_args); + * } + * @endcode + * + */ +PARCEvent *parcEvent_Create(PARCEventScheduler *parcEventScheduler, int fileDescriptor, PARCEventType events, PARCEvent_Callback *callback, void *callbackArgs); + +/** + * Start an event instance. + * + * @param [in] parcEvent instance to start + * @returns -1 on error, 0 on success if nothing changed in the parcEvent backend, and 1 on success if something did. + * + * Example: + * @code + * startEvent(PARCEvent *parcEvent) + * { + * return parcEvent_Start(parcEvent); + * } + * @endcode + * + */ +int parcEvent_Start(PARCEvent *parcEvent); + +/** + * Stop a parcEvent instance. + * + * @param [in] parcEvent instance to stop + * @returns -1 on error, 0 on success. + * + * Example: + * @code + * removeEvent(PARCEvent *parcEvent) + * { + * return parcEvent_Stop(parcEvent); + * } + * @endcode + * + */ +int parcEvent_Stop(PARCEvent *parcEvent); + +/** + * Poll if an event is available to process + * + * @param [in] parcEvent instance to stop + * @param [in] event type to poll for + * @returns -1 on error, 0 on success. + * + * Example: + * @code + * pollReadEvent(PARCEvent *parcEvent) + * { + * return parcEvent_Poll(parcEvent, PARCEvent_ReadEvent); + * } + * @endcode + * + */ +int parcEvent_Poll(PARCEvent *parcEvent, PARCEventType event); + +/** + * Destroy a parcEvent instance. + * + * @param [in] parcEvent address of instance to destroy. + * + * Example: + * @code + * { + * parcEvent_Destroy(&parcEvent); + * } + * @endcode + * + */ +void parcEvent_Destroy(PARCEvent **parcEvent); + +/** + * Set a parcEvent instances priority. + * + * @param [in] parcEvent instance to modify + * @param [in] priority to set to + * @returns -1 on error, 0 on success. + * + * Example: + * @code + * { + * return parcEvent_SetPriority(parcEvent, priority); + * } + * @endcode + * + */ +int parcEvent_SetPriority(PARCEvent *parcEvent, PARCEventPriority priority); + +/** + * Turn on debugging flags and messages + * + * Example: + * @code + * { + * parcEvent_EnableDebug(); + * } + * @endcode + * + */ +void parcEvent_EnableDebug(void); + +/** + * Turn off debugging flags and messages + * + * Example: + * @code + * { + * parcEvent_DisableDebug(); + * } + * @endcode + * + */ +void parcEvent_DisableDebug(void); +#endif // libparc_parc_Event_h diff --git a/libparc/parc/algol/parc_EventBuffer.c b/libparc/parc/algol/parc_EventBuffer.c new file mode 100644 index 00000000..49ba3354 --- /dev/null +++ b/libparc/parc/algol/parc_EventBuffer.c @@ -0,0 +1,282 @@ +/* + * 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 <LongBow/runtime.h> + +#include "internal_parc_Event.h" +#include <parc/algol/parc_Event.h> +#include <parc/algol/parc_EventBuffer.h> +#include <parc/algol/parc_FileOutputStream.h> +#include <parc/logging/parc_Log.h> +#include <parc/logging/parc_LogReporterFile.h> + +static PARCLog *parcEventBufferDebugLog = NULL; + +#define parcEventBuffer_LogDebug(parcEvent, ...) \ + if (parcEventBufferDebugLog) \ + parcLog_Debug(parcEventBufferDebugLog, __VA_ARGS__) + +/** + * Current implementation based on top of libevent2 + */ + +#include <event2/buffer.h> + +/** + * @typedef PARCEventBuffer + * @brief A structure containing private libevent state data variables + * + * The evbuffer either points to an evbuffer owned by the bufferevent, or an + * allocated evbuffer (allocated_evbuffer) that is our responsibility to destroy. + */ +struct PARCEventBuffer { + struct evbuffer *evbuffer; + struct evbuffer *allocated_evbuffer; +}; + +PARCEventBuffer * +parcEventBuffer_Create(void) +{ + internal_parc_initializeLibevent(); + struct evbuffer *new_evbuffer = evbuffer_new(); + assertNotNull(new_evbuffer, "libevent returned a null evbuffer.\n"); + + PARCEventBuffer *parcEventBuffer = parcMemory_AllocateAndClear(sizeof(PARCEventBuffer)); + assertNotNull(parcEventBuffer, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(PARCEventBuffer)); + parcEventBuffer->allocated_evbuffer = new_evbuffer; + parcEventBuffer->evbuffer = parcEventBuffer->allocated_evbuffer; + + parcEventBuffer_LogDebug(parcEventBuffer, "parcEventBuffer_Create() = %p\n", parcEventBuffer); + + return parcEventBuffer; +} + +void +parcEventBuffer_Destroy(PARCEventBuffer **parcEventBuffer) +{ + parcEventBuffer_LogDebug((*parcEventBuffer), "parcEventBuffer_Destroy(parcEventBuffer=%p)\n", *parcEventBuffer); + assertNotNull(parcEventBuffer, "parcEventBuffer_Destroy was passed a null parcEventBuffer pointer\n"); + assertNotNull(*parcEventBuffer, "parcEventBuffer_Destroy was passed a null parcEventBuffer\n"); + + // Destroy allocated eveventBuffer if it was allocated by us, otherwise it's owned by bufferevent + if ((*parcEventBuffer)->allocated_evbuffer) { + parcEventBuffer_LogDebug((*parcEventBuffer), "parcEventBuffer_Destroy(parcEventBuffer=%p) freeing evbuffer %p\n", *parcEventBuffer, (*parcEventBuffer)->allocated_evbuffer); + evbuffer_free((*parcEventBuffer)->allocated_evbuffer); + } + parcMemory_Deallocate((void **) parcEventBuffer); + parcEventBuffer_LogDebug((*parcEventBuffer), "parcEventBuffer_Destroy() buffer already deallocated\n"); +} + +bool +parcEventBuffer_IsValid(const PARCEventBuffer *eventBuffer) +{ + bool result = false; + + if (eventBuffer != NULL) { + if (eventBuffer->evbuffer) { + result = true; + } + } + + return result; +} + +void +parcEventBuffer_AssertValid(const PARCEventBuffer *eventBuffer) +{ + assertTrue(parcEventBuffer_IsValid(eventBuffer), + "PARCEventBuffer@%p is not valid.", (void *) eventBuffer); +} + +size_t +parcEventBuffer_GetLength(PARCEventBuffer *parcEventBuffer) +{ + parcEventBuffer_LogDebug(parcEventBuffer, "parcEventBuffer_GetLength(parcEventBuffer=%p)\n", parcEventBuffer); +// parcEventBuffer_OptionalAssertValid(parcEventBuffer); + assertNotNull(parcEventBuffer, "parcEventBuffer_GetLength was passed a null parcEventBuffer\n"); + + if (parcEventBuffer->evbuffer) { + return evbuffer_get_length(parcEventBuffer->evbuffer); + } else { + return 0; + } +} + +uint8_t * +parcEventBuffer_Pullup(PARCEventBuffer *parcEventBuffer, ssize_t size) +{ + parcEventBuffer_LogDebug(parcEventBuffer, "parcEventBuffer_Pullup(parcEventBuffer=%p,size=%zx)\n", parcEventBuffer, size); + + parcEventBuffer_OptionalAssertValid(parcEventBuffer); +// assertNotNull(parcEventBuffer, "parcEventBuffer_Pullup was passed a null parcEventBuffer\n"); +// assertNotNull(parcEventBuffer->evbuffer, "parcEventBuffer_Pullup was passed a null libevent evbuffer\n"); + + return evbuffer_pullup(parcEventBuffer->evbuffer, (ev_ssize_t) size); +} + +int +parcEventBuffer_ReadIntoBuffer(PARCEventBuffer *source, PARCEventBuffer *destination, size_t length) +{ + parcEventBuffer_OptionalAssertValid(source); + parcEventBuffer_OptionalAssertValid(destination); +// assertNotNull(source, "parcEventBuffer_ReadIntoBuffer was passed a null source buffer\n"); +// assertNotNull(source->evbuffer, "parcEventBuffer_ReadIntoBuffer was passed a null source evbuffer\n"); +// assertNotNull(destination, "parcEventBuffer_ReadIntoBuffer was passed a null destination buffer\n"); +// assertNotNull(destination->evbuffer, "parcEventBuffer_ReadIntoBuffer was passed a null destination evbuffer\n"); + + return evbuffer_remove_buffer(source->evbuffer, destination->evbuffer, length); +} + +int +parcEventBuffer_Read(PARCEventBuffer *readBuffer, void *data, size_t length) +{ + parcEventBuffer_OptionalAssertValid(readBuffer); +// assertNotNull(readBuffer, "parcEventBuffer_Read was passed a null buffer\n"); +// assertNotNull(readBuffer->evbuffer, "parcEventBuffer_Read was passed a null libevent evbuffer\n"); + + if (data == NULL) { + return evbuffer_drain(readBuffer->evbuffer, length); + } else { + return evbuffer_remove(readBuffer->evbuffer, data, length); + } +} + +int +parcEventBuffer_copyOut(PARCEventBuffer *readBuffer, void *data_out, size_t length) +{ + assertNotNull(data_out, "parcEventBuffer_Copy was passed a null data_out buffer\n"); + parcEventBuffer_OptionalAssertValid(readBuffer); + return evbuffer_copyout(readBuffer->evbuffer, data_out, length); +} + +int +parcEventBuffer_WriteToFileDescriptor(PARCEventBuffer *writeBuffer, int fd, ssize_t length) +{ + parcEventBuffer_OptionalAssertValid(writeBuffer); + +// assertNotNull(writeBuffer, "parcEventBuffer_WriteToFileDescriptor was passed a null buffer\n"); +// assertNotNull(writeBuffer->evbuffer, "parcEventBuffer_WriteToFileDescriptor was passed a null libevent evbuffer\n"); + + return evbuffer_write_atmost(writeBuffer->evbuffer, fd, length); +} + +int +parcEventBuffer_ReadFromFileDescriptor(PARCEventBuffer *readBuffer, int fd, size_t length) +{ + parcEventBuffer_OptionalAssertValid(readBuffer); +// assertNotNull(readBuffer, "parcEventBuffer_ReadFromFileDescriptor was passed a null buffer\n"); +// assertNotNull(readBuffer->evbuffer, "parcEventBuffer_ReadFromFileDescriptor was passed a null libevent evbuffer\n"); + + return evbuffer_read(readBuffer->evbuffer, fd, (int) length); +} + +void +parcEventBuffer_FreeLine(PARCEventBuffer *readBuffer, char **line) +{ + parcEventBuffer_OptionalAssertValid(readBuffer); +// assertNotNull(readBuffer, "parcEventBuffer_ReadLine was passed a null readBuffer\n"); +// assertNotNull(readBuffer->evbuffer, "parcEventBuffer_ReadLine was passed a null libevent evbuffer\n"); + + parcMemory_Deallocate((void **) line); +} + +char * +parcEventBuffer_ReadLine(PARCEventBuffer *readBuffer, size_t *length) +{ + parcEventBuffer_OptionalAssertValid(readBuffer); +// assertNotNull(readBuffer, "parcEventBuffer_ReadLine was passed a null readBuffer\n"); +// assertNotNull(readBuffer->evbuffer, "parcEventBuffer_ReadLine was passed a null libevent evbuffer\n"); + + return evbuffer_readln(readBuffer->evbuffer, length, EVBUFFER_EOL_CRLF); +} + +int +parcEventBuffer_AppendBuffer(PARCEventBuffer *source, PARCEventBuffer *destination) +{ + parcEventBuffer_OptionalAssertValid(source); + parcEventBuffer_OptionalAssertValid(destination); +// assertNotNull(source, "parcEventBuffer_AppendBuffer was passed a null source parcEventBuffer\n"); +// assertNotNull(destination, "parcEventBuffer_AppendBuffer was passed a null destination parcEventBuffer\n"); +// assertNotNull(source->evbuffer, "parcEventBuffer_AppendBuffer was passed a null source libevent evbuffer\n"); +// assertNotNull(destination->evbuffer, "parcEventBuffer_AppendBuffer was passed a null destination libevent evbuffer\n"); + + return evbuffer_add_buffer(destination->evbuffer, source->evbuffer); +} + +int +parcEventBuffer_Append(PARCEventBuffer *parcEventBuffer, void *data, size_t length) +{ + parcEventBuffer_OptionalAssertValid(parcEventBuffer); +// assertNotNull(parcEventBuffer, "parcEventBuffer_Append was passed a null parcEventBuffer\n"); +// assertNotNull(parcEventBuffer->evbuffer, "parcEventBuffer_Append was passed a null libevent evbuffer\n"); + assertNotNull(data, "parcEventBuffer_Append was passed a null data buffer\n"); + + return evbuffer_add(parcEventBuffer->evbuffer, data, length); +} + +int +parcEventBuffer_Prepend(PARCEventBuffer *readBuffer, void *data, size_t length) +{ + parcEventBuffer_OptionalAssertValid(readBuffer); +// assertNotNull(readBuffer->evbuffer, "parcEventBuffer_Prepend was passed a null libevent evbuffer\n"); +// assertNotNull(readBuffer, "parcEventBuffer_Prepend was passed a null buffer\n"); + assertNotNull(data, "parcEventBuffer_Prepend was passed a null data buffer\n"); + + return evbuffer_prepend(readBuffer->evbuffer, data, length); +} + +PARCEventBuffer * +parcEventBuffer_GetQueueBufferInput(PARCEventQueue *queue) +{ + assertNotNull(queue, "parcEventBuffer_GetQueueBufferInput was passed a null queue\n"); + + PARCEventBuffer *parcEventBuffer = parcMemory_AllocateAndClear(sizeof(PARCEventBuffer)); + assertNotNull(parcEventBuffer, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(PARCEventBuffer)); + + parcEventBuffer->evbuffer = internal_parcEventQueue_GetEvInputBuffer(queue); + parcEventBuffer_LogDebug(parcEventBuffer, "parcEventBuffer_GetQueueBufferInput(queue=%p)\n", queue); + + return parcEventBuffer; +} + +PARCEventBuffer * +parcEventBuffer_GetQueueBufferOutput(PARCEventQueue *queue) +{ + assertNotNull(queue, "parcEventBuffer_GetQueueBufferOutput was passed a null queue\n"); + + PARCEventBuffer *parcEventBuffer = parcMemory_AllocateAndClear(sizeof(PARCEventBuffer)); + assertNotNull(parcEventBuffer, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(PARCEventBuffer)); + + parcEventBuffer->evbuffer = internal_parcEventQueue_GetEvOutputBuffer(queue); + parcEventBuffer_LogDebug(parcEventBuffer, "parcEventBuffer_GetQueueBufferInput(queue=%p)\n", queue); + + return parcEventBuffer; +} + +void +parcEventBuffer_EnableDebug(PARCLog *logger) +{ + parcEventBufferDebugLog = logger; +} + +void +parcEventBuffer_DisableDebug(void) +{ + parcEventBufferDebugLog = NULL; +} diff --git a/libparc/parc/algol/parc_EventBuffer.h b/libparc/parc/algol/parc_EventBuffer.h new file mode 100755 index 00000000..8e82a5e6 --- /dev/null +++ b/libparc/parc/algol/parc_EventBuffer.h @@ -0,0 +1,387 @@ +/* + * 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. + */ + +/** + * @file parc_EventBuffer.h + * @ingroup events + * @brief Buffered event management + * + * Provides a facade implementing many regularly available event functions. + * This is an interface that software implementors may use to substitute + * different kinds of underlying implementations of these event management functions. + * Notable examples are libevent and libev. + * + */ +#ifndef libparc_parc_EventBuffer_h +#define libparc_parc_EventBuffer_h + +#include <parc/algol/parc_EventQueue.h> + +#ifdef PARCLibrary_DISABLE_VALIDATION +# define parcEventBuffer_OptionalAssertValid(_instance_) +#else +# define parcEventBuffer_OptionalAssertValid(_instance_) parcEventBuffer_AssertValid(_instance_) +#endif + +/** + * @typedef PARCEventBuffer + * @brief A structure containing private libevent state data variables + */ +typedef struct PARCEventBuffer PARCEventBuffer; + +/** + * Create an event buffer instance. + * + * @returns A pointer to a new PARCEventBuffer instance. + * + * Example: + * @code + * { + * PARCEventBuffer *parcEventBuffer = parcEventBuffer_Create(); + * } + * @endcode + * + */ +PARCEventBuffer *parcEventBuffer_Create(void); + +/** + * Destroy a network parcEventBuffer instance. + * + * The address of the event instance is passed in. + * + * @param [in] parcEventBuffer - The address of the instance to destroy. + * + * Example: + * @code + * { + * parcEventBuffer_Destroy(&parcEventBuffer) + * } + * @endcode + * + */ +void parcEventBuffer_Destroy(PARCEventBuffer **parcEventBuffer); + +/** + * Determine if an instance of `PARCEventBuffer` is valid. + * + * Valid means the internal state of the type is consistent with its required current or future behaviour. + * This may include the validation of internal instances of types. + * + * @param [in] eventBuffer A pointer to a `PARCEventBuffer` instance. + * + * @return true The instance is valid. + * @return false The instance is not valid. + * + * Example: + * @code + * { + * PARCEventBuffer *instance = parcEventBuffer_Create(); + * + * if (parcEventBuffer_IsValid(instance)) { + * printf("Instance is valid.\n"); + * } + * } + * @endcode + */ +bool parcEventBuffer_IsValid(const PARCEventBuffer *eventBuffer); + +/** + * Assert that the given `PARCEventBuffer` instance is valid. + * + * @param [in] eventBuffer A pointer to a valid PARCEventBuffer instance. + * + * Example: + * @code + * { + * PARCEventBuffer *a = parcEventBuffer_Create(); + * + * parcEventBuffer_AssertValid(a); + * + * printf("Instance is valid.\n"); + * + * parcEventBuffer_Release(&b); + * } + * @endcode + */ +void parcEventBuffer_AssertValid(const PARCEventBuffer *eventBuffer); + +/** + * Get the input parcEventBuffer instance from the Queue + * + * @param [in] parcEventQueue - The queue to obtain the input parcEventBuffer from + * @returns A pointer to the a PARCEventBuffer instance. + * + * Example: + * @code + * { + * PARCEventBuffer *parcEventBuffer = parcEventBuffer_GetQueueBufferInput(eventQueue); + * } + * @endcode + * + */ +PARCEventBuffer *parcEventBuffer_GetQueueBufferInput(PARCEventQueue *parcEventQueue); + +/** + * Get the output parcEventBuffer instance from the Queue + * + * @param [in] parcEventQueue - The queue to obtain the output parcEventBuffer from + * @returns A pointer to the a PARCEventBuffer instance. + * + * Example: + * @code + * { + * PARCEventBuffer *parcEventBuffer = parcEventBuffer_GetQueueBufferOutput(parcEventQueue); + * } + * @endcode + * + */ +PARCEventBuffer *parcEventBuffer_GetQueueBufferOutput(PARCEventQueue *parcEventQueue); + +/** + * Get the data length of the associated buffer + * + * @param [in] parcEventBuffer - The buffer to obtain the length from + * @returns The length of data within the buffer, 0 if the internal buffer has been freed + * + * Example: + * @code + * { + * int length = parcEventBuffer_GetLength(parcEventBuffer); + * } + * @endcode + * + */ +size_t parcEventBuffer_GetLength(PARCEventBuffer *parcEventBuffer); + +/** + * Consolidate data in the associated parcEventBuffer + * + * @param [in] parcEventBuffer - The buffer to consolidate + * @param [in] size - The length of data to consolidate, -1 linearizes the entire buffer + * @returns A pointer to the first byte in the buffer. + * + * Example: + * @code + * { + * unsigned char *ptr = parcEventBuffer_Pullup(parcEventBuffer, size); + * } + * @endcode + * + */ +uint8_t *parcEventBuffer_Pullup(PARCEventBuffer *parcEventBuffer, ssize_t size); + +/** + * Read data from the associated parcEventBuffer + * + * @param [in] parcEventBuffer - The parcEventBuffer to read from + * @param [in] data - The memory to read into, NULL to discard data + * @param [in] length - The number of bytes of data to be read + * @returns Number of bytes read, -1 on failure. + * + * Example: + * @code + * { + * int bytesRead = parcEventBuffer_Read(parcEventBuffer, destination, bytesToRead); + * } + * @endcode + * + */ +int parcEventBuffer_Read(PARCEventBuffer *parcEventBuffer, void *data, size_t length); + +/** + * Read data from the associated parcEventBuffer without delete them from the buffer. + * Data can not be NULL + * + * @param [in] parcEventBuffer - The parcEventBuffer to read from + * @param [in] data - The memory to read into + * @param [in] length - The number of bytes of data to be read + * @returns Number of bytes read, -1 on failure. + * + */ +int parcEventBuffer_copyOut(PARCEventBuffer *readBuffer, void *data_out, size_t length); + + +/** + * Read from a file descriptor into the end of a buffer + * + * @param [in] parcEventBuffer - The buffer to read from + * @param [in] fd - The file descriptor to read from + * @param [in] length - The number of bytes of data to be read + * @returns The number of bytes read, 0 on EOF and -1 on error. + * + * Example: + * @code + * { + * int bytesTransfered = parcEventBuffer_ReadFromFileDescriptor(parcEventBuffer, fd, length); + * } + * @endcode + * + */ +int parcEventBuffer_ReadFromFileDescriptor(PARCEventBuffer *parcEventBuffer, int fd, size_t length); + +/** + * Write to a file descriptor from a buffer + * + * @param [in] parcEventBuffer - The buffer to read from + * @param [in] fd - The file descriptor to write to + * @param [in] length - The number of bytes of data to be read (-1 for all). + * @returns The number of bytes read, 0 on EOF and -1 on error. + * + * Example: + * @code + * { + * int bytesTransfered = parcEventBuffer_WriteToFileDescriptor(parcEventBuffer, fd, length); + * } + * @endcode + * + */ +int parcEventBuffer_WriteToFileDescriptor(PARCEventBuffer *parcEventBuffer, int fd, ssize_t length); + +/** + * Append one PARCEventBuffer to another + * + * @param [in] source - The buffer to append + * @param [in] destination - The buffer to append to + * @returns 0 on success, -1 on failure + * + * Example: + * @code + * { + * int result = parcEventBuffer_AppendBuffer(source, destination); + * } + * @endcode + * + */ +int parcEventBuffer_AppendBuffer(PARCEventBuffer *source, PARCEventBuffer *destination); + +/** + * Append bytes to a parcEventBuffer + * + * @param [in] parcEventBuffer - The buffer to write into + * @param [in] sourceData - The data to append + * @param [in] length - The number of bytes of data to be added + * @returns 0 on success, -1 on failure + * + * Example: + * @code + * { + * int result = parcEventBuffer_Append(parcEventBuffer, sourceData, length); + * } + * @endcode + * + */ +int parcEventBuffer_Append(PARCEventBuffer *parcEventBuffer, void *sourceData, size_t length); + +/** + * Prepend data to the associated parcEventBuffer + * + * @param [in] parcEventBuffer - The parcEventBuffer to prepend to + * @param [in] sourceData - The data to prepend + * @param [in] length - The number of bytes of data to be added + * @returns 0 on success, -1 on failure + * + * Example: + * @code + * { + * int result = parcEventBuffer_Read(parcEventBuffer, sourceData, length); + * } + * @endcode + * + */ +int parcEventBuffer_Prepend(PARCEventBuffer *parcEventBuffer, void *sourceData, size_t length); + +/** + * Move data from one buffer to another + * + * @param [in] sourceEventBuffer - The buffer to read from + * @param [in] destinationEventBuffer - The buffer to move into + * @param [in] length - The number of bytes of data to be moved + * @returns The number of bytes moved on success, -1 on failure + * + * Example: + * @code + * { + * int bytesMoved = parcEventBuffer_Create(sourceEventBuffer, destinationEventBuffer, length); + * } + * @endcode + * + */ +int parcEventBuffer_ReadIntoBuffer(PARCEventBuffer *sourceEventBuffer, PARCEventBuffer *destinationEventBuffer, size_t length); + +/** + * Read a text line terminated by an optional carriage return, followed by a single linefeed + * + * @param [in] parcEventBuffer - The buffer to read from + * @param [in] length - The number of bytes of data to be moved + * @returns A newly allocated null terminated string, which must be freed by the caller + * + * Example: + * @code + * { + * char *text = parcEventBuffer_ReadLine(parcEventBuffer, length); + * ... + * parcMemory_Deallocate((void *)&text); + * } + * @endcode + * + */ +char *parcEventBuffer_ReadLine(PARCEventBuffer *parcEventBuffer, size_t *length); + +/** + * Free a text line returned from parcEventBuffer_ReadLine + * + * @param [in] parcEventBuffer - The buffer to read from + * @param [in] line address of returned value from previous call to parcEventBuffer_ReadLine + * + * Example: + * @code + * { + * char *text = parcEventBuffer_ReadLine(parcEventBuffer, length); + * ... + * parcEventBuffer_FreeLine(parcEventBuffer, &text); + * } + * @endcode + * + */ +void parcEventBuffer_FreeLine(PARCEventBuffer *parcEventBuffer, char **line); + +/** + * Turn on debugging flags and messages + * + * @param [in] logger - the log to write debug messages to + * + * Example: + * @code + * { + * parcEventBuffer_EnableDebug(logger); + * } + * @endcode + * + */ +void parcEventBuffer_EnableDebug(PARCLog *logger); + +/** + * Turn off debugging flags and messages + * + * Example: + * @code + * { + * parcEventBuffer_DisableDebug(); + * } + * @endcode + * + */ +void parcEventBuffer_DisableDebug(void); +#endif // libparc_parc_EventBuffer_h diff --git a/libparc/parc/algol/parc_EventQueue.c b/libparc/parc/algol/parc_EventQueue.c new file mode 100755 index 00000000..5d656a12 --- /dev/null +++ b/libparc/parc/algol/parc_EventQueue.c @@ -0,0 +1,353 @@ +/* + * 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 <errno.h> + +#include <LongBow/runtime.h> + +#include "internal_parc_Event.h" +#include <parc/algol/parc_EventScheduler.h> +#include <parc/algol/parc_EventQueue.h> +#include <parc/algol/parc_FileOutputStream.h> +#include <parc/logging/parc_Log.h> +#include <parc/logging/parc_LogReporterFile.h> + +static int _parc_event_queue_debug_enabled = 0; + +#define parcEventQueue_LogDebug(parcEventQueue, ...) \ + if (_parc_event_queue_debug_enabled) \ + parcLog_Debug(parcEventScheduler_GetLogger(parcEventQueue->eventScheduler), __VA_ARGS__) + +/** + * Current implementation based on top of libevent2 + */ + +#include <event2/buffer.h> +#include <event2/bufferevent.h> + +/** + * @typedef PARCEventQueue + * @brief A structure containing private event state + */ +struct PARCEventQueue { + // Event scheduler we have been queued with + PARCEventScheduler *eventScheduler; + + struct bufferevent *buffereventBuffer; + // Interpose on bufferevent callbacks + PARCEventQueue_Callback *readCallback; + void *readUserData; + PARCEventQueue_Callback *writeCallback; + void *writeUserData; + PARCEventQueue_EventCallback *eventCallback; + void *eventUserData; +}; + +struct PARCEventQueuePair { + PARCEventQueue *up; + PARCEventQueue *down; +}; + +static void +_parc_queue_read_callback(struct bufferevent *bev, void *ptr) +{ + PARCEventQueue *parcEventQueue = (PARCEventQueue *) ptr; + parcEventQueue_LogDebug(parcEventQueue, + "_parc_queue_read_callback(bev=%p,ptr->buffereventBuffer=%p,parcEventQueue=%p)\n", + bev, parcEventQueue->buffereventBuffer, parcEventQueue); + assertNotNull(parcEventQueue->readCallback, "parcEvent read callback called when NULL"); + + parcEventQueue->readCallback(parcEventQueue, PARCEventType_Read, parcEventQueue->readUserData); +} + +static void +_parc_queue_write_callback(struct bufferevent *bev, void *ptr) +{ + PARCEventQueue *parcEventQueue = (PARCEventQueue *) ptr; + parcEventQueue_LogDebug(parcEventQueue, + "_parc_queue_write_callback(bev=%p,ptr->buffereventBuffer=%p,parcEventQueue=%p)\n", + bev, parcEventQueue->buffereventBuffer, parcEventQueue); + assertNotNull(parcEventQueue->writeCallback, "parcEvent write callback called when NULL"); + + parcEventQueue->writeCallback(parcEventQueue, PARCEventType_Write, parcEventQueue->writeUserData); +} + +static void +_parc_queue_event_callback(struct bufferevent *bev, short events, void *ptr) +{ + PARCEventQueue *parcEventQueue = (PARCEventQueue *) ptr; + int errno_forwarded = errno; + parcEventQueue_LogDebug(parcEventQueue, + "_parc_queue_event_callback(bev=%p,events=%x,errno=%d,ptr->buffereventBuffer=%p,parcEventQueue=%p)\n", + bev, events, errno, parcEventQueue->buffereventBuffer, parcEventQueue); + assertNotNull(parcEventQueue->eventCallback, "parcEvent event callback called when NULL"); + + errno = errno_forwarded; + parcEventQueue->eventCallback(parcEventQueue, internal_bufferevent_type_to_PARCEventQueueEventType(events), parcEventQueue->eventUserData); +} + +void +parcEventQueue_SetCallbacks(PARCEventQueue *parcEventQueue, + PARCEventQueue_Callback *readCallback, + PARCEventQueue_Callback *writeCallback, + PARCEventQueue_EventCallback *eventCallback, + void *user_data) +{ + parcEventQueue_LogDebug(parcEventQueue, + "parcEventQueue_SetCallbacks(event=%p(buffer=%p),readcb=%p,writecb=%p,eventcb=%p,user_data=%p)\n", + parcEventQueue, parcEventQueue->buffereventBuffer, + readCallback, writeCallback, eventCallback, + user_data); + + parcEventQueue->readCallback = readCallback; + parcEventQueue->readUserData = user_data; + parcEventQueue->writeCallback = writeCallback; + parcEventQueue->writeUserData = user_data; + parcEventQueue->eventCallback = eventCallback; + parcEventQueue->eventUserData = user_data; + bufferevent_setcb(parcEventQueue->buffereventBuffer, + (readCallback) ? _parc_queue_read_callback : NULL, + (writeCallback) ? _parc_queue_write_callback : NULL, + (eventCallback) ? _parc_queue_event_callback : NULL, + parcEventQueue); +} + +PARCEventQueue * +parcEventQueue_Create(PARCEventScheduler *eventScheduler, int fd, PARCEventQueueOption flags) +{ + assertNotNull(eventScheduler, "parcEventQueue_Create passed a NULL scheduler instance."); + PARCEventQueue *parcEventQueue = parcMemory_AllocateAndClear(sizeof(PARCEventQueue)); + assertNotNull(parcEventQueue, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(PARCEventQueue)); + parcEventQueue->eventScheduler = eventScheduler; + + // + // PARCEventQueue_CloseOnFree + // we close the underlying file descriptor/bufferevent/whatever + // when this bufferevent is freed. + // + // PARCEventQueue_DeferCallbacks + // callbacks are run deferred in the event loop. + // + parcEventQueue->buffereventBuffer = bufferevent_socket_new(parcEventScheduler_GetEvBase(eventScheduler), fd, + internal_PARCEventQueueOption_to_bufferevent_options(flags)); + assertNotNull(parcEventQueue->buffereventBuffer, + "Got null from bufferevent_socket_new for socket %d", fd); + + parcEventQueue_LogDebug(parcEventQueue, + "parcEventQueue_Create(eventScheduler=%p,libevent_base=%p) = %p\n", + eventScheduler, + parcEventScheduler_GetEvBase(eventScheduler), + parcEventQueue); + + return parcEventQueue; +} + +void +parcEventQueue_Destroy(PARCEventQueue **parcEventQueue) +{ + parcEventQueue_LogDebug((*parcEventQueue), "parcEventQueue_Destroy(ptr=%p)\n", *parcEventQueue); + assertNotNull((*parcEventQueue)->buffereventBuffer, "parcEventQueue_Destroy passed a null buffer!"); + + bufferevent_free((*parcEventQueue)->buffereventBuffer); + parcMemory_Deallocate((void *) parcEventQueue); +} + +int +parcEventQueue_SetFileDescriptor(PARCEventQueue *parcEventQueue, int fd) +{ + return bufferevent_setfd(parcEventQueue->buffereventBuffer, fd); +} + +int +parcEventQueue_GetFileDescriptor(PARCEventQueue *parcEventQueue) +{ + return bufferevent_getfd(parcEventQueue->buffereventBuffer); +} + +PARCEventType +parcEventQueue_GetEnabled(PARCEventQueue *event) +{ + return internal_libevent_type_to_PARCEventType(bufferevent_get_enabled(event->buffereventBuffer)); +} + +void +parcEventQueue_Enable(PARCEventQueue *parcEventQueue, PARCEventType types) +{ + bufferevent_enable(parcEventQueue->buffereventBuffer, internal_PARCEventType_to_libevent_type(types)); +} + +void +parcEventQueue_Disable(PARCEventQueue *parcEventQueue, PARCEventType types) +{ + bufferevent_disable(parcEventQueue->buffereventBuffer, internal_PARCEventType_to_libevent_type(types)); +} + +int +parcEventQueue_ConnectSocket(PARCEventQueue *instance, struct sockaddr *address, int addrlen) +{ + return bufferevent_socket_connect(instance->buffereventBuffer, address, addrlen); +} + +int +parcEventQueue_Flush(PARCEventQueue *parcEventQueue, PARCEventType types) +{ + return bufferevent_flush(parcEventQueue->buffereventBuffer, internal_PARCEventType_to_libevent_type(types), BEV_NORMAL); +} + +int +parcEventQueue_Finished(PARCEventQueue *parcEventQueue, PARCEventType types) +{ + return bufferevent_flush(parcEventQueue->buffereventBuffer, internal_PARCEventType_to_libevent_type(types), BEV_FINISHED); +} + +void +parcEventQueue_SetWatermark(PARCEventQueue *parcEventQueue, PARCEventType types, size_t low, size_t high) +{ + parcEventQueue_LogDebug(parcEventQueue, "parcEventQueue->buffereventBuffer=%p\n", parcEventQueue->buffereventBuffer); + bufferevent_setwatermark(parcEventQueue->buffereventBuffer, internal_PARCEventType_to_libevent_type(types), low, high); +} + +int +parcEventQueue_Printf(PARCEventQueue *parcEventQueue, const char *fmt, ...) +{ + struct evbuffer *buffer = bufferevent_get_output(parcEventQueue->buffereventBuffer); + assertNotNull(buffer, "bufferevent_get_output returned NULL"); + + va_list ap; + + va_start(ap, fmt); + int result = evbuffer_add_vprintf(buffer, fmt, ap); + va_end(ap); + return result; +} + +int +parcEventQueue_Read(PARCEventQueue *parcEventQueue, void *data, size_t dataLength) +{ + return bufferevent_read(parcEventQueue->buffereventBuffer, data, dataLength); +} + +int +parcEventQueue_Write(PARCEventQueue *parcEventQueue, void *data, size_t dataLength) +{ + return bufferevent_write(parcEventQueue->buffereventBuffer, data, dataLength); +} + +int +parcEventQueue_SetPriority(PARCEventQueue *eventQueue, PARCEventPriority priority) +{ + bufferevent_priority_set(eventQueue->buffereventBuffer, internal_PARCEventPriority_to_libevent_priority(priority)); + return 0; +} + +PARCEventQueuePair * +parcEventQueue_CreateConnectedPair(PARCEventScheduler *eventScheduler) +{ + assertNotNull(eventScheduler, "parcEventQueue_CreateConnectedPair must be passed a valid Event Scheduler"); + PARCEventQueuePair *parcEventQueuePair = parcMemory_AllocateAndClear(sizeof(PARCEventQueuePair)); + assertNotNull(parcEventQueuePair, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(PARCEventQueuePair)); + + parcEventQueuePair->up = parcMemory_AllocateAndClear(sizeof(PARCEventQueue)); + parcEventQueuePair->up->eventScheduler = eventScheduler; + parcEventQueue_LogDebug(parcEventQueuePair->up, + "up instance parcEventQueue_Create(eventScheduler=%p,libevent_parcEventQueue=%p) = %p\n", + eventScheduler, + parcEventScheduler_GetEvBase(eventScheduler), + parcEventQueuePair->up); + assertNotNull(parcEventQueuePair->up, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(PARCEventQueue)); + + parcEventQueuePair->down = parcMemory_AllocateAndClear(sizeof(PARCEventQueue)); + parcEventQueuePair->down->eventScheduler = eventScheduler; + parcEventQueue_LogDebug(parcEventQueuePair->down, + "down instance parcEventQueue_Create(eventScheduler=%p,libevent_parcEventQueue=%p) = %p\n", + eventScheduler, + parcEventScheduler_GetEvBase(eventScheduler), + parcEventQueuePair->down); + assertNotNull(parcEventQueuePair->down, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(PARCEventQueue)); + + struct bufferevent *evpair[2]; + int result = bufferevent_pair_new(parcEventScheduler_GetEvBase(eventScheduler), 0, evpair); + if (result != 0) { + parcMemory_Deallocate((void **) &(parcEventQueuePair->up)); + parcMemory_Deallocate((void **) &(parcEventQueuePair->down)); + parcMemory_Deallocate((void **) &parcEventQueuePair); + return NULL; + } + + parcEventQueuePair->up->buffereventBuffer = evpair[0]; + parcEventQueuePair->down->buffereventBuffer = evpair[1]; + + (void) parcEventQueue_SetPriority(parcEventQueuePair->up, PARCEventPriority_Normal); + (void) parcEventQueue_SetPriority(parcEventQueuePair->down, PARCEventPriority_Normal); + + return parcEventQueuePair; +} + +void +parcEventQueue_DestroyConnectedPair(PARCEventQueuePair **queuePair) +{ + parcEventQueue_LogDebug((*queuePair)->up, + "parcEventQueue_DestroyPair(up ptr=%p)\n", + (*queuePair)->up); + parcEventQueue_LogDebug((*queuePair)->down, + "parcEventQueue_DestroyPair(down ptr=%p)\n", + (*queuePair)->down); + + bufferevent_free((*queuePair)->up->buffereventBuffer); + bufferevent_free((*queuePair)->down->buffereventBuffer); + + parcMemory_Deallocate((void **) &((*queuePair)->up)); + parcMemory_Deallocate((void **) &((*queuePair)->down)); + parcMemory_Deallocate((void **) queuePair); +} + +PARCEventQueue * +parcEventQueue_GetConnectedUpQueue(PARCEventQueuePair *queuePair) +{ + return queuePair->up; +} + +PARCEventQueue * +parcEventQueue_GetConnectedDownQueue(PARCEventQueuePair *queuePair) +{ + return queuePair->down; +} + +struct evbuffer * +internal_parcEventQueue_GetEvInputBuffer(PARCEventQueue *queue) +{ + return bufferevent_get_input(queue->buffereventBuffer); +} + +struct evbuffer * +internal_parcEventQueue_GetEvOutputBuffer(PARCEventQueue *queue) +{ + return bufferevent_get_output(queue->buffereventBuffer); +} + +void +parcEventQueue_EnableDebug(void) +{ + _parc_event_queue_debug_enabled = 1; +} + +void +parcEventQueue_DisableDebug(void) +{ + _parc_event_queue_debug_enabled = 0; +} diff --git a/libparc/parc/algol/parc_EventQueue.h b/libparc/parc/algol/parc_EventQueue.h new file mode 100644 index 00000000..282df302 --- /dev/null +++ b/libparc/parc/algol/parc_EventQueue.h @@ -0,0 +1,515 @@ +/* + * 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. + */ + +/** + * @file parc_EventQueue.h + * @ingroup events + * @brief Queue buffer events + * + * Provides a facade implementing many regularly available event functions. + * This is an interface that software implementors may use to substitute + * different kinds of underlying implementations of these event management functions. + * Notable examples are libevent and libev. + * + */ +#ifndef libparc_parc_EventQueue_h +#define libparc_parc_EventQueue_h + +#include <sys/types.h> +#include <sys/socket.h> + +#include <parc/algol/parc_Event.h> + +/** + * Current implementation based on top of libevent2 + */ + +/** + * @typedef PARCEventQueue + * @brief A structure containing private libevent state data variables + */ +typedef struct PARCEventQueue PARCEventQueue; + +/** + * @typedef PARCEventQueuePair + * @brief A structure containing private libevent state data for connected queue pairs + */ +typedef struct PARCEventQueuePair PARCEventQueuePair; + +/** + * @typedef PARCEventQueueEventType + * @brief An enumeration of queue event types + */ +typedef enum { + PARCEventQueueEventType_Reading = 0x01, + PARCEventQueueEventType_Writing = 0x02, + PARCEventQueueEventType_EOF = 0x10, + PARCEventQueueEventType_Error = 0x20, + PARCEventQueueEventType_Timeout = 0x40, + PARCEventQueueEventType_Connected = 0x80 +} PARCEventQueueEventType; + +/** + * @typedef PARCEventQueue_Options + * @brief A structure queue flags + */ +typedef enum { + PARCEventQueueOption_CloseOnFree = 0x01, + PARCEventQueueOption_DeferCallbacks = 0x04 +} PARCEventQueueOption; + +/** + * @typedef PARCEventQueue_Callback + * @brief A definition for callback function arguments + */ +typedef void (PARCEventQueue_Callback)(PARCEventQueue *event, PARCEventType type, void *user_data); + +/** + * @typedef PARCEventQueue_EventCallback + * @brief A definition for callback function arguments + */ +typedef void (PARCEventQueue_EventCallback)(PARCEventQueue *event, PARCEventQueueEventType type, void *user_data); + +/** + * Create a buffer event handler instance. + * + * The event instance is passed in. Options can be either, both or none of the following. + * + * PARCEventQueue_CloseOnFree + * The underlying file descriptor is closed when this event is freed. + * + * PARCEventQueue_DeferCallbacks + * Callbacks are run deferred in the scheduler. + * + * @param [in] eventScheduler - The scheduler instance base. + * @param [in] fd the file descriptor to monitor + * @param [in] options as described in flags above + * @returns A pointer to the a PARCEventQueue instance. + * + * Example: + * @code + * { + * PARCEventQueue *event = parcEventQueue_Create(eventScheduler, fd, PARCEventQueue_CloseOnFree); + * } + * @endcode + * + */ +PARCEventQueue *parcEventQueue_Create(PARCEventScheduler *eventScheduler, int fd, PARCEventQueueOption options); + +/** + * Destroy a buffer event handler instance. + * + * The event instance is passed in. + * + * @param [in] eventQueue - The address of the instance to destroy. + * + * Example: + * @code + * { + * parcEventQueue_Destroy(&eventQueue); + * } + * @endcode + * + */ +void parcEventQueue_Destroy(PARCEventQueue **eventQueue); + +/** + * Enable events on an instance. + * + * The event instance is passed in. + * + * @param [in] event - queue instance to enable. + * @param [in] types - the event(s) to enable. + * + * Example: + * @code + * { + * parcEventQueue_Enable(bufferEvent, PARCEvent_ReadEvent); + * } + * @endcode + * + */ +void parcEventQueue_Enable(PARCEventQueue *event, PARCEventType types); + +/** + * Get enable events on an instance. + * + * The event instance is passed in. + * + * @param [in] queue - the instance to return enabled events from + * @returns mask of events which are enabled. + * + * Example: + * @code + * { + * PARCEventType *events = parcEventQueue_GetEnable(queue); + * } + * @endcode + * + */ +PARCEventType parcEventQueue_GetEnabled(PARCEventQueue *queue); + +/** + * Disable events on an instance. + * + * The event instance is passed in. + * + * @param [in] queue - instance to disable event on. + * @param [in] types - the events to disable. + * + * Example: + * @code + * { + * parcEventQueue_Disable(queue, types); + * } + * @endcode + * + */ +void parcEventQueue_Disable(PARCEventQueue *queue, PARCEventType types); + +/** + * Set callbacks on a buffer event instance. + * + * The event instance is passed in. + * You can disable a callback by passing NULL instead of the callback function. + * NB: all the callback functions on a bufferevent share a single user_data + * value, so changing user_data will affect all of them. + * + * @param [in] eventInstance - event instance + * @param [in] readCallback - callback for read events + * @param [in] writeCallback - callback for write events + * @param [in] eventCallback - callback for non-read/write events + * @param [in] user_data - data passed along in callback arguments + * + * Example: + * @code + * void Callback(PARCEventType type, void *user_data) + * { + * printf("Received event of type=%d\n", type); + * } + * ... + * { + * ... + * parcEventQueue_SetCallbacks(eventInstance, Callback, NULL, NULL, user_data); + * parcEventQueue_Enable(eventInstance, PARCEventType_Read); + * } + * @endcode + * + */ +void parcEventQueue_SetCallbacks(PARCEventQueue *eventInstance, + PARCEventQueue_Callback *readCallback, + PARCEventQueue_Callback *writeCallback, + PARCEventQueue_EventCallback *eventCallback, + void *user_data); + +/** + * Flush events on a queue + * + * @param [in] queue instance to flush + * @param [in] types the type(s) of events to flush + * @returns 0 on success, -1 on failure + * + * Example: + * @code + * { + * int result = parcEventQueue_Flush(queue, PARCEventType_Write); + * } + * @endcode + * + */ +int parcEventQueue_Flush(PARCEventQueue *queue, PARCEventType types); + +/** + * Finialized flush of events on a queue + * + * @param [in] queue instance to flush + * @param [in] types the type(s) of events to flush + * @returns 0 if no data was flushed, 1 if some data was flushed, -1 on failure + * + * Example: + * @code + * { + * int result = parcEventQueue_Finished(queue, PARCEventType_Write); + * } + * @endcode + * + */ +int parcEventQueue_Finished(PARCEventQueue *queue, PARCEventType types); + +/** + * Set watermark boundries on a queue + * + * @param [in] queue - queue instance to set watermark on + * @param [in] types - the events to set watermark on + * @param [in] low - the low watermark value + * @param [in] high - the high watermark value + * + * Example: + * @code + * { + * parcEventQueue_SetWatermark(queue, PARCEventType_Read, 0, MAXPATHLEN); + * } + * @endcode + * + */ +void parcEventQueue_SetWatermark(PARCEventQueue *queue, PARCEventType types, size_t low, size_t high); + +/** + * Add formatted text to the end of a queue + * + * @param [in] queue - queue instance to write to + * @param [in] fmt - printf arguments + * + * Example: + * @code + * { + * parcEventQueue_Printf(queue, "%s\n", "Hello world."); + * } + * @endcode + * + */ +int parcEventQueue_Printf(PARCEventQueue *queue, const char *fmt, ...); + +/** + * Set the associated file descriptor on a queue + * + * @param [in] queue instance set to monitor this descriptor + * @param [in] fd file descriptor + * @returns 0 on success, -1 on failure + * + * Example: + * @code + * { + * int result = parcEventQueue_SetFileDescriptor(queue, STDIN_FILENO); + * } + * @endcode + * + */ +int parcEventQueue_SetFileDescriptor(PARCEventQueue *queue, int fd); + +/** + * Get the associated file descriptor on a queue + * + * @param [in] queue instance set to monitor this descriptor + * @returns file descriptor on success, -1 on failure + * + * Example: + * @code + * { + * int fileDescriptor = parcEventQueue_GetFileDescriptor(queue); + * } + * @endcode + * + */ +int parcEventQueue_GetFileDescriptor(PARCEventQueue *queue); + +/** + * Read data from the queue output + * + * @param [in] queue instance to read from + * @param [in] data to read into + * @param [in] dataLength length of data to read + * @returns 0 on success, -1 on failure + * + * Example: + * @code + * { + * int result = parcEventQueue_Read(queue, data, length); + * } + * @endcode + * + */ +int parcEventQueue_Read(PARCEventQueue *queue, void *data, size_t dataLength); + +/** + * Add data to the queue output + * + * @param [in] queue instance to add to + * @param [in] data to write + * @param [in] dataLength length of data to write + * @returns 0 on success, -1 on failure + * + * Example: + * @code + * { + * int result = parcEventQueue_Write(queue, data, length); + * } + * @endcode + * + */ +int parcEventQueue_Write(PARCEventQueue *queue, void *data, size_t dataLength); + +/** + * Attach an launch a socket on a queue + * + * @param [in] queue instance to attach socket to + * @param [in] address socket data + * @param [in] addressLength socket data length + * @returns 0 on success, -1 on failure + * + * Example: + * @code + * { + * struct sockaddr_un addr_unix; + * memset(&addr_unix, 0, sizeof(addr_unix)); + * addr_unix.sun_family = AF_UNIX; + * strcpy(addr_unix.sun_path, sock_name); + * int result = parcEventQueue_ConnectSocket(queue, addr_unix, sizeof(addr_unix)); + * } + * @endcode + * + */ +int parcEventQueue_ConnectSocket(PARCEventQueue *queue, struct sockaddr *address, int addressLength); + +/** + * Set queue priority + * + * @param [in] queue instance to modify + * @param [in] priority queue priority + * @returns 0 on success, -1 on failure + * + * Example: + * @code + * { + * parcEvent_Enable(queue, PARCEventQueuePriority_Normal); + * } + * @endcode + * + */ +int parcEventQueue_SetPriority(PARCEventQueue *queue, PARCEventPriority priority); + +/** + * Create a pair of connected queues + * + * @param [in] eventScheduler event scheduler instance + * @returns a queue pair instance + * + * Example: + * @code + * { + * PARCEventQueuePair *pair = parcEventQueue_CreateConnectedPair(eventScheduler); + * } + * @endcode + * + */ +PARCEventQueuePair *parcEventQueue_CreateConnectedPair(PARCEventScheduler *eventScheduler); + +/** + * Destroy a connected queue pair + * + * @param [in] queuePair queue pair instance address to destroy + * + * Example: + * @code + * { + * parcEventQueuePairCreateparcEventQueue_DestroyConnectedPair(&queuePair); + * } + * @endcode + * + */ +void parcEventQueue_DestroyConnectedPair(PARCEventQueuePair **queuePair); + +/** + * Return the downward queue of a pair + * + * @param [in] queuePair queue pair instance address to destroy + * + * Example: + * @code + * { + * PARCEventQueue *downQueue = parcEventQueue_GetConnectedDownQueue(queuePair); + * } + * @endcode + * + */ +PARCEventQueue *parcEventQueue_GetConnectedDownQueue(PARCEventQueuePair *queuePair); + +/** + * Return the upward queue of a pair + * + * @param [in] queuePair queue pair instance address to destroy + * + * Example: + * @code + * { + * PARCEventQueue *upQueue = parcEventQueue_GetConnectedUpQueue(queuePair); + * } + * @endcode + * + */ +PARCEventQueue *parcEventQueue_GetConnectedUpQueue(PARCEventQueuePair *queuePair); + +/** + * Private Internal Function - return internal input buffer of a queue + * + * The event instance is passed in. + * + * @param [in] queue the event queue + * @returns private evbuffer pointer + * + * Example: + * @code + * { + * struct evbuffer *evbuffer = internal_parcEventQueue_GetEvInputBuffer(queue); + * } + * @endcode + * + */ +struct evbuffer *internal_parcEventQueue_GetEvInputBuffer(PARCEventQueue *queue); + +/** + * Private Internal Function - return internal output buffer of a queue + * + * The event instance is passed in. + * + * @param [in] queue the event queue + * @returns private evbuffer pointer + * + * Example: + * @code + * { + * struct evbuffer *evbuffer = internal_parcEventQueue_GetEvOutputBuffer(queue); + * } + * @endcode + * + */ +struct evbuffer *internal_parcEventQueue_GetEvOutputBuffer(PARCEventQueue *queue); + +/** + * Turn on debugging flags and messages + * + * Example: + * @code + * { + * parcEventQueue_EnableDebug(); + * } + * @endcode + * + */ +void parcEventQueue_EnableDebug(void); + +/** + * Turn off debugging flags and messages + * + * Example: + * @code + * { + * parcEventQueue_DisableDebug(); + * } + * @endcode + * + */ +void parcEventQueue_DisableDebug(void); +#endif // libparc_parc_EventQueue_h diff --git a/libparc/parc/algol/parc_EventScheduler.c b/libparc/parc/algol/parc_EventScheduler.c new file mode 100755 index 00000000..55de5c52 --- /dev/null +++ b/libparc/parc/algol/parc_EventScheduler.c @@ -0,0 +1,171 @@ +/* + * 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 <LongBow/runtime.h> + +#include <stdio.h> +#include <unistd.h> + +#include "internal_parc_Event.h" +#include <parc/algol/parc_EventScheduler.h> +#include <parc/algol/parc_FileOutputStream.h> +#include <parc/logging/parc_Log.h> +#include <parc/logging/parc_LogReporterFile.h> + +/** + * Current implementation based on top of libevent2 + */ +#include <event2/event.h> + +static int _parc_event_scheduler_debug_enabled = 0; + +#define parcEventScheduler_LogDebug(parcEventScheduler, ...) \ + if (_parc_event_scheduler_debug_enabled) \ + parcLog_Debug(parcEventScheduler->log, __VA_ARGS__) + +struct PARCEventScheduler { + /** + * Base of the libevent manager. + */ + struct event_base *evbase; + PARCLog *log; +}; + +static PARCLog * +_parc_logger_create(void) +{ + PARCFileOutputStream *fileOutput = parcFileOutputStream_Create(dup(STDOUT_FILENO)); + PARCOutputStream *output = parcFileOutputStream_AsOutputStream(fileOutput); + parcFileOutputStream_Release(&fileOutput); + + PARCLogReporter *reporter = parcLogReporterFile_Create(output); + parcOutputStream_Release(&output); + + PARCLog *log = parcLog_Create("localhost", "test_parc_Log", NULL, reporter); + parcLogReporter_Release(&reporter); + + parcLog_SetLevel(log, PARCLogLevel_All); + return log; +} + +void * +parcEventScheduler_GetEvBase(PARCEventScheduler *parcEventScheduler) +{ + return (void *) parcEventScheduler->evbase; +} + +PARCEventScheduler * +parcEventScheduler_Create(void) +{ + internal_parc_initializeLibevent(); + + PARCEventScheduler *parcEventScheduler = parcMemory_Allocate(sizeof(PARCEventScheduler)); + assertNotNull(parcEventScheduler, "parcMemory_Allocate(%zu) returned NULL", sizeof(PARCEventScheduler)); + + // Initialize libevent base pointer. + parcEventScheduler->evbase = event_base_new(); + + assertNotNull(parcEventScheduler->evbase, "Could not obtain an event base!"); + int result = event_base_priority_init(parcEventScheduler->evbase, PARCEventPriority_NumberOfPriorities); + assertTrue(result == 0, "Could not set scheduler priorities (%d)", result); + + parcEventScheduler->log = _parc_logger_create(); + assertNotNull(parcEventScheduler->log, "Could not create parc logger"); + + parcEventScheduler_LogDebug(parcEventScheduler, "parcEventScheduler_Create() = %p\n", parcEventScheduler); + + return parcEventScheduler; +} + +int +parcEventScheduler_Start(PARCEventScheduler *parcEventScheduler, PARCEventSchedulerDispatchType type) +{ + parcEventScheduler_LogDebug(parcEventScheduler, "parcEventScheduler_Start(%p, %d)\n", parcEventScheduler, type); + assertNotNull(parcEventScheduler, "parcEventScheduler_Start must be passed a valid base parcEventScheduler!"); + int result = event_base_loop(parcEventScheduler->evbase, + internal_PARCEventSchedulerDispatchType_to_eventloop_options(type)); + return result; +} + +int +parcEventScheduler_DispatchBlocking(PARCEventScheduler *parcEventScheduler) +{ + return parcEventScheduler_Start(parcEventScheduler, PARCEventSchedulerDispatchType_Blocking); +} + +int +parcEventScheduler_DispatchNonBlocking(PARCEventScheduler *parcEventScheduler) +{ + return parcEventScheduler_Start(parcEventScheduler, PARCEventSchedulerDispatchType_NonBlocking); +} + +int +parcEventScheduler_Stop(PARCEventScheduler *parcEventScheduler, struct timeval *delay) +{ + parcEventScheduler_LogDebug(parcEventScheduler, "parcEventScheduler_Stop(%p, %p)\n", parcEventScheduler, delay); + assertNotNull(parcEventScheduler, "parcEventScheduler_Stop must be passed a valid base parcEventScheduler!"); + int result = event_base_loopexit(parcEventScheduler->evbase, delay); + return result; +} + +int +parcEventScheduler_Abort(PARCEventScheduler *parcEventScheduler) +{ + parcEventScheduler_LogDebug(parcEventScheduler, "parcEventScheduler_Abort(%p)\n", parcEventScheduler); + assertNotNull(parcEventScheduler, "parcEventScheduler_Abort must be passed a valid base parcEventScheduler!"); + int result = event_base_loopbreak(parcEventScheduler->evbase); + return result; +} + +static int _event_enable_debug_mode_called = 0; + +void +parcEventScheduler_EnableDebug(void) +{ + _parc_event_scheduler_debug_enabled = 1; + if (_event_enable_debug_mode_called == 0) { + event_enable_debug_mode(); + _event_enable_debug_mode_called = 1; + } +} + +void +parcEventScheduler_DisableDebug(void) +{ + _parc_event_scheduler_debug_enabled = 0; +} + +void +parcEventScheduler_Destroy(PARCEventScheduler **parcEventScheduler) +{ + parcEventScheduler_LogDebug((*parcEventScheduler), "parcEventScheduler_Destroy(%p)\n", *parcEventScheduler); + + assertNotNull(*parcEventScheduler, "parcEventScheduler_Destroy must be passed a valid base parcEventScheduler!"); + assertNotNull((*parcEventScheduler)->evbase, "parcEventScheduler_Destroy passed a NULL event base member!"); + + event_base_free((*parcEventScheduler)->evbase); + parcLog_Release(&((*parcEventScheduler)->log)); + parcMemory_Deallocate((void **) parcEventScheduler); +} + +PARCLog * +parcEventScheduler_GetLogger(PARCEventScheduler *parcEventScheduler) +{ + return parcEventScheduler->log; +} diff --git a/libparc/parc/algol/parc_EventScheduler.h b/libparc/parc/algol/parc_EventScheduler.h new file mode 100644 index 00000000..d84ac5e4 --- /dev/null +++ b/libparc/parc/algol/parc_EventScheduler.h @@ -0,0 +1,224 @@ +/* + * 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. + */ + +/** + * @file parc_EventScheduler.h + * @ingroup events + * @brief Event scheduler + * + * Provides a facade implementing many regularly available event functions. + * This is an interface that software implementors may use to substitute + * different kinds of underlying implementations of these event management functions. + * Notable examples are libevent and libev. + * + */ +#ifndef libparc_parc_EventScheduler_h +#define libparc_parc_EventScheduler_h + +#include <stdlib.h> +#include <stdint.h> + +/* + * Currently implemented using libevent + */ + +#include <parc/algol/parc_Memory.h> +#include <parc/logging/parc_Log.h> + +/** + * @typedef PARCEventScheduler + * @brief A structure containing private event state + */ +struct PARCEventScheduler; +typedef struct PARCEventScheduler PARCEventScheduler; + +typedef enum { + PARCEventSchedulerDispatchType_Blocking = 0x00, + PARCEventSchedulerDispatchType_LoopOnce = 0x01, + PARCEventSchedulerDispatchType_NonBlocking = 0x02, +} PARCEventSchedulerDispatchType; + +/** + * Create a new parcEventScheduler instance. + * + * A new parcEventScheduler instance is returned. + * + * @returns A pointer to the a PARCEvent instance. + * + * Example: + * @code + * { + * PARCEvent *parcEventScheduler = parcEvent_Create(); + * } + * @endcode + * + */ +PARCEventScheduler *parcEventScheduler_Create(void); + +/** + * Start the event eventScheduler + * + * @param [in] parcEventScheduler - The parcEventScheduler instance to start. + * @param type - The mode of dispatch for this `PARCEventScheduler` instance. + * @returns 0 on success, -1 on failure + * + * Example: + * @code + * { + * result = parcEventScheduler_Start(parcEventScheduler, PARCEventScheduler_Blocking); + * } + * @endcode + * + */ +int parcEventScheduler_Start(PARCEventScheduler *parcEventScheduler, PARCEventSchedulerDispatchType type); + +/** + * Dispatch the event scheduler to process any pending events, blocking until + * some events have been triggered and then processed. + * + * @param [in] parcEventScheduler - The parcEventScheduler instance to run. + * @returns 0 on success, -1 on failure + * + * Example: + * @code + * { + * result = parcEventScheduler_DispatchBlocking(parcEventScheduler, PARCEventScheduler_Blocking); + * } + * @endcode + */ +int parcEventScheduler_DispatchBlocking(PARCEventScheduler *parcEventScheduler); + +/** + * Dispatch the event scheduler to process any pending events. + * + * If there are no pending events then the function will immediately return. + * + * @param [in] parcEventScheduler - The parcEventScheduler instance to run. + * @returns 0 on success, -1 on failure + * + * Example: + * @code + * { + * result = parcEventScheduler_DispatchNonBlocking(parcEventScheduler, PARCEventScheduler_Blocking); + * } + * @endcode + * + */ +int parcEventScheduler_DispatchNonBlocking(PARCEventScheduler *parcEventScheduler); + +/** + * Stop the event parcEventScheduler + * + * @param [in] parcEventScheduler instance to stop scheduling. + * @param [in] delay time to wait before stopping, 0 for stop now + * @returns 0 on success, -1 on failure + * + * Example: + * @code + * { + * result = parcEventScheduler_Stop(parcEventScheduler, timeout); + * } + * @endcode + * + */ +int parcEventScheduler_Stop(PARCEventScheduler *parcEventScheduler, struct timeval *delay); + +/** + * Immediately abort the event parcEventScheduler + * + * @param [in] parcEventScheduler instance to abort scheduling. + * @returns 0 on success, -1 on failure + * + * Example: + * @code + * { + * result = parcEventScheduler_Abort(parcEventScheduler); + * } + * @endcode + * + */ +int parcEventScheduler_Abort(PARCEventScheduler *parcEventScheduler); + +/** + * Destroy a parcEventScheduler instance. + * + * The address of the parcEventScheduler instance is passed in. + * + * @param [in] parcEventScheduler address of instance to destroy. + * + * Example: + * @code + * { + * parcEventScheduler_Destroy(&parcEventScheduler); + * } + * @endcode + * + */ +void parcEventScheduler_Destroy(PARCEventScheduler **parcEventScheduler); + +/** + * Turn on debugging flags and messages + * + * Example: + * @code + * { + * parcEventScheduler_EnableDebug(); + * } + * @endcode + * + */ +void parcEventScheduler_EnableDebug(void); + +/** + * Turn off debugging flags and messages + * + * Example: + * @code + * { + * parcEventScheduler_DisableDebug(); + * } + * @endcode + * + */ +void parcEventScheduler_DisableDebug(void); + +/** + * Internal libevent data accessor function. + * + * THIS IS FOR INTERNAL USE ONLY. USE WITH CAUTION. + * + * Example: + * @code + * { + * parcEventScheduler_GetEvBase(parcEventScheduler); + * } + * @endcode + * + */ +void *parcEventScheduler_GetEvBase(PARCEventScheduler *parcEventScheduler); + +/** + * Logger accessor function + * + * Example: + * @code + * { + * PARCLog *logger = parcEventScheduler_GetLogger(parcEventScheduler); + * } + * @endcode + * + */ +PARCLog *parcEventScheduler_GetLogger(PARCEventScheduler *parcEventScheduler); +#endif // libparc_parc_EventScheduler_h diff --git a/libparc/parc/algol/parc_EventSignal.c b/libparc/parc/algol/parc_EventSignal.c new file mode 100755 index 00000000..7b8d1191 --- /dev/null +++ b/libparc/parc/algol/parc_EventSignal.c @@ -0,0 +1,127 @@ +/* + * 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 <LongBow/runtime.h> + +#include "internal_parc_Event.h" +#include <parc/algol/parc_EventScheduler.h> +#include <parc/algol/parc_EventSignal.h> +#include <parc/algol/parc_FileOutputStream.h> +#include <parc/logging/parc_Log.h> +#include <parc/logging/parc_LogReporterFile.h> + +static int _parc_event_signal_debug_enabled = 0; + +#define parcEventSignal_LogDebug(parcEventSignal, ...) \ + if (_parc_event_signal_debug_enabled) \ + parcLog_Debug(parcEventScheduler_GetLogger(parcEventSignal->eventScheduler), __VA_ARGS__) + +/** + * Current implementation based on top of libevent2 + */ +#include <event2/event.h> +#include <event2/util.h> + +struct PARCEventSignal { + /** + * The event instance. + */ + struct event *event; + + // Event scheduler we have been queued with + PARCEventScheduler *eventScheduler; + + PARCEventSignal_Callback *callback; + void *callbackUserData; +}; + +static void +_parc_event_signal_callback(evutil_socket_t fd, short flags, void *context) +{ + PARCEventSignal *parcEventSignal = (PARCEventSignal *) context; + parcEventSignal_LogDebug(parcEventSignal, + "_parc_event_signal_callback(fd=%x,flags=%x,parcEventSignal=%p)\n", + fd, flags, parcEventSignal); + parcEventSignal->callback((int) fd, internal_libevent_type_to_PARCEventType(flags), + parcEventSignal->callbackUserData); +} + +PARCEventSignal * +parcEventSignal_Create(PARCEventScheduler *eventScheduler, int signal, PARCEventType flags, PARCEvent_Callback *callback, void *callbackArgs) +{ + PARCEventSignal *parcEventSignal = parcMemory_Allocate(sizeof(PARCEventSignal)); + assertNotNull(parcEventSignal, "parcMemory_Allocate(%zu) returned NULL", sizeof(PARCEventSignal)); + + parcEventSignal->eventScheduler = eventScheduler; + parcEventSignal->callback = callback; + parcEventSignal->callbackUserData = callbackArgs; + + parcEventSignal->event = event_new(parcEventScheduler_GetEvBase(eventScheduler), signal, + internal_PARCEventType_to_libevent_type(flags), + _parc_event_signal_callback, parcEventSignal); + assertNotNull(parcEventSignal->event, "Could not create a new event!"); + + parcEventSignal_LogDebug(parcEventSignal, + "parcEventSignal_Create(base=%p,signal=%x,flags=%x,cb=%p,args=%p) = %p\n", + parcEventScheduler_GetEvBase(eventScheduler), signal, flags, + callback, callbackArgs, parcEventSignal); + return parcEventSignal; +} + +int +parcEventSignal_Start(PARCEventSignal *parcEventSignal) +{ + parcEventSignal_LogDebug(parcEventSignal, "parcEventSignal_Start(event=%p)\n", parcEventSignal); + assertNotNull(parcEventSignal, "parcEventStart_Signal must be passed a valid event!"); + + int result = event_add(parcEventSignal->event, NULL); + return result; +} + +int +parcEventSignal_Stop(PARCEventSignal *parcEventSignal) +{ + parcEventSignal_LogDebug(parcEventSignal, "parcEventSignal_Stop(event=%p)\n", parcEventSignal); + assertNotNull(parcEventSignal, "parcEvent_Stop must be passed a valid event!"); + + int result = event_del(parcEventSignal->event); + return result; +} + +void +parcEventSignal_Destroy(PARCEventSignal **parcEventSignal) +{ + parcEventSignal_LogDebug((*parcEventSignal), "parcEventSignal_Destroy(event=%p)\n", parcEventSignal); + assertNotNull(*parcEventSignal, "parcEvent_Destroy must be passed a valid parcEventSignal!"); + assertNotNull((*parcEventSignal)->event, "parcEvent_Destroy passed a null event!"); + event_free((*parcEventSignal)->event); + parcMemory_Deallocate((void **) parcEventSignal); +} + +void +parcEventSignal_EnableDebug(void) +{ + _parc_event_signal_debug_enabled = 1; +} + +void +parcEventSignal_DisableDebug(void) +{ + _parc_event_signal_debug_enabled = 0; +} diff --git a/libparc/parc/algol/parc_EventSignal.h b/libparc/parc/algol/parc_EventSignal.h new file mode 100755 index 00000000..e289f506 --- /dev/null +++ b/libparc/parc/algol/parc_EventSignal.h @@ -0,0 +1,136 @@ +/* + * 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. + */ + +/** + * @file parc_EventSignal.h + * @ingroup events + * @brief Signal events + * + * Provides a facade implementing many regularly available event functions. + * This is an interface that software implementors may use to substitute + * different kinds of underlying implementations of these event management functions. + * Notable examples are libevent and libev. + * + */ +#ifndef libparc_parc_EventSignal +#define libparc_parc_EventSignal + +#include <parc/algol/parc_Event.h> + +typedef void (PARCEventSignal_Callback)(int fd, PARCEventType type, void *user_data); + +typedef struct PARCEventSignal PARCEventSignal; + +/** + * Create a new signal event instance. + * + * The new event instance is returned. + * + * @param [in] parcEventScheduler - scheduler instance + * @param [in] signal - signal to catch + * @param [in] flags - event flags + * @param [in] callback - event callback + * @param [in] callbackArguments - private arguments passed to callback + * @returns A pointer to the a PARCEventSignal instance. + * + * Example: + * @code + * { + * PARCEventSignal *parcEventSignal = parcEventSignal_Create(parcEventScheduler, SIGUSR1, 0, callback, callbackArguments); + * } + * @endcode + * + */ +PARCEventSignal *parcEventSignal_Create(PARCEventScheduler *parcEventScheduler, + int signal, PARCEventType flags, + PARCEventSignal_Callback *callback, + void *callbackArguments); + +/** + * Prepare a parcEventSignal instance to be scheduled. + * + * @returns -1 on error, 0 on success if nothing changed in the event backend, and 1 on success if something did. + * @param [in] parcEventSignal the newly created event instance. + * + * Example: + * @code + * addEvent(PARCEventSignal *parcEventSignal) + * { + * int result = parcEventSignal_Start(parcEventSignal); + * } + * @endcode + * + */ +int parcEventSignal_Start(PARCEventSignal *parcEventSignal); + +/** + * Stop a parcEventSignal instance. + * + * @param [in] parcEventSignal - The newly created event instance. + * + * Example: + * @code + * removeEvent(PARCEventSignal *parcEventSignal) + * { + * parcEventSignal_Stop(parcEventSignal); + * } + * @endcode + * + */ +int parcEventSignal_Stop(PARCEventSignal *parcEventSignal); + +/** + * Destroy a parcEventSignal instance. + * + * The event instance is passed in. + * + * @param [in] parcEventSignal The instance to destroy. + * + * Example: + * @code + * { + * parcEventSignal_Destroy(&eparcEventSignal vent); + * } + * @endcode + * + */ +void parcEventSignal_Destroy(PARCEventSignal **parcEventSignal); + +/** + * Turn on debugging flags and messages + * + * Example: + * @code + * { + * parcEventSignal_EnableDebug(); + * } + * @endcode + * + */ +void parcEventSignal_EnableDebug(void); + +/** + * Turn off debugging flags and messages + * + * Example: + * @code + * { + * parcEventSignal_DisableDebug(); + * } + * @endcode + * + */ +void parcEventSignal_DisableDebug(void); +#endif // libparc_parc_EventSignal_h diff --git a/libparc/parc/algol/parc_EventSocket.c b/libparc/parc/algol/parc_EventSocket.c new file mode 100755 index 00000000..dcb2fd69 --- /dev/null +++ b/libparc/parc/algol/parc_EventSocket.c @@ -0,0 +1,140 @@ +/* + * 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 <LongBow/runtime.h> + +#include <parc/algol/parc_EventScheduler.h> +#include <parc/algol/parc_EventSocket.h> +#include <parc/algol/parc_FileOutputStream.h> +#include <parc/logging/parc_Log.h> +#include <parc/logging/parc_LogReporterFile.h> + +static int _parc_event_socket_debug_enabled = 0; + +#define parcEventSocket_LogDebug(parcEventSocket, ...) \ + if (_parc_event_socket_debug_enabled) \ + parcLog_Debug(parcEventScheduler_GetLogger(parcEventSocket->eventScheduler), __VA_ARGS__) + +/** + * Current implementation based on top of libevent2 + */ + +#include <sys/errno.h> +#include <event2/listener.h> + +/** + * @typedef PARCEventSocket + * @brief A structure containing private event state + */ +struct PARCEventSocket { + struct evconnlistener *listener; + + // Event scheduler we have been queued with + PARCEventScheduler *eventScheduler; + + // Interpose on EventSocket callbacks + PARCEventSocket_Callback *socketCallback; + void *socketUserData; + PARCEventSocket_ErrorCallback *socketErrorCallback; + void *socketErrorUserData; +}; + +static void +_parc_evconn_callback(struct evconnlistener *listener, evutil_socket_t fd, + struct sockaddr *address, int socklen, void *ctx) +{ + PARCEventSocket *parcEventSocket = (PARCEventSocket *) ctx; + parcEventSocket_LogDebug(parcEventSocket, "_parc_evconn_callback(fd=%d,,parcEventSocket=%p)\n", fd, parcEventSocket); + + parcEventSocket->socketCallback((int) fd, address, socklen, parcEventSocket->socketUserData); +} + +static void +_parc_evconn_error_callback(struct evconnlistener *listener, void *ctx) +{ + PARCEventSocket *parcEventSocket = (PARCEventSocket *) ctx; + + int error = EVUTIL_SOCKET_ERROR(); + char *errorString = evutil_socket_error_to_string(error); + parcEventSocket_LogDebug(parcEventSocket, + "_parc_evconn_error_callback(error=%d,errorString=%s,parcEventSocket=%p)\n", + error, errorString, parcEventSocket); + + parcEventSocket->socketErrorCallback(parcEventSocket->eventScheduler, + error, errorString, parcEventSocket->socketErrorUserData); +} + +PARCEventSocket * +parcEventSocket_Create(PARCEventScheduler *eventScheduler, + PARCEventSocket_Callback *callback, + PARCEventSocket_ErrorCallback *errorCallback, + void *userData, const struct sockaddr *sa, int socklen) +{ + PARCEventSocket *parcEventSocket = parcMemory_AllocateAndClear(sizeof(PARCEventSocket)); + assertNotNull(parcEventSocket, "parcMemory_Allocate(%zu) returned NULL", sizeof(PARCEventSocket)); + + parcEventSocket->eventScheduler = eventScheduler; + parcEventSocket->socketCallback = callback; + parcEventSocket->socketErrorCallback = errorCallback; + parcEventSocket->socketUserData = userData; + parcEventSocket->socketErrorUserData = userData; + parcEventSocket->listener = evconnlistener_new_bind(parcEventScheduler_GetEvBase(eventScheduler), + _parc_evconn_callback, parcEventSocket, + LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE, -1, + sa, socklen); + if (parcEventSocket->listener == NULL) { + parcLog_Error(parcEventScheduler_GetLogger(eventScheduler), + "Libevent evconnlistener_new_bind error (%d): %s", + errno, strerror(errno)); + parcEventSocket_Destroy(&parcEventSocket); + return NULL; + } + + if (errorCallback) { + evconnlistener_set_error_cb(parcEventSocket->listener, _parc_evconn_error_callback); + } + parcEventSocket_LogDebug(parcEventSocket, + "parcEventSocket_Create(cb=%p,args=%p) = %p\n", + callback, userData, parcEventSocket); + return parcEventSocket; +} + +void +parcEventSocket_Destroy(PARCEventSocket **socketEvent) +{ + assertNotNull(*socketEvent, "parcEventSocket_Destroy must be passed a valid socketEvent!"); + + if ((*socketEvent)->listener) { + evconnlistener_free((*socketEvent)->listener); + } + parcEventSocket_LogDebug((*socketEvent), "parcEventSocket_Destroy(%p)\n", *socketEvent); + parcMemory_Deallocate((void **) socketEvent); +} + +void +parcEventSocket_EnableDebug(void) +{ + _parc_event_socket_debug_enabled = 1; +} + +void +parcEventSocket_DisableDebug(void) +{ + _parc_event_socket_debug_enabled = 0; +} diff --git a/libparc/parc/algol/parc_EventSocket.h b/libparc/parc/algol/parc_EventSocket.h new file mode 100755 index 00000000..48a11b25 --- /dev/null +++ b/libparc/parc/algol/parc_EventSocket.h @@ -0,0 +1,116 @@ +/* + * 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. + */ + +/** + * @file parc_EventSocket.h + * @ingroup events + * @brief Socket events + * + * Provides a facade implementing many regularly available event functions. + * This is an interface that software implementors may use to substitute + * different kinds of underlying implementations of these event management functions. + * Notable examples are libevent and libev. + * + */ +#ifndef libparc_parc_EventSocket_h +#define libparc_parc_EventSocket_h + +#include <sys/types.h> +#include <sys/socket.h> + +/** + * Current implementation based on top of libevent2 + */ + +#include <parc/algol/parc_EventScheduler.h> +#include <parc/algol/parc_Event.h> + +typedef struct PARCEventSocket PARCEventSocket; + +typedef void (PARCEventSocket_Callback)(int fd, struct sockaddr *address, + int socklen, void *user_data); +typedef void (PARCEventSocket_ErrorCallback)(PARCEventScheduler *, + int error, char *errorString, + void *user_data); + +/** + * Create a socket event handler instance. + * + * The event instance is passed in. + * + * @param [in] parcEventScheduler the scheduler instance + * @param [in] callback the callback function. + * @param [in] errorCallback the error callback function. + * @param [in] userData pointer to private arguments for instance callback function + * @param [in] sa is the socket address to bind to (INET, INET6, LOCAL) + * @param [in] socklen is the sizeof the actual sockaddr (e.g. sizeof(sockaddr_un)) + * @returns A pointer to a new PARCEventSocket instance. + * + * Example: + * @code + * { + * } + * @endcode + * + */ +PARCEventSocket *parcEventSocket_Create(PARCEventScheduler *parcEventScheduler, + PARCEventSocket_Callback *callback, + PARCEventSocket_ErrorCallback *errorCallback, + void *userData, + const struct sockaddr *sa, int socklen); + +/** + * Destroy a socket event handler instance. + * + * The event instance is passed in. + * + * @param [in] parcEventSocket the address of the instance to destroy. + * + * Example: + * @code + * { + * PARCEvent *event = parcEventSocket_Destroy(&parcEventSocket); + * } + * @endcode + * + */ +void parcEventSocket_Destroy(PARCEventSocket **parcEventSocket); + +/** + * Turn on debugging flags and messages + * + * Example: + * @code + * { + * parcEventSocket_EnableDebug(); + * } + * @endcode + * + */ +void parcEventSocket_EnableDebug(void); + +/** + * Turn off debugging flags and messages + * + * Example: + * @code + * { + * parcEventSocket_DisableDebug(); + * } + * @endcode + * + */ +void parcEventSocket_DisableDebug(void); +#endif // libparc_parc_EventSocket_h diff --git a/libparc/parc/algol/parc_EventTimer.c b/libparc/parc/algol/parc_EventTimer.c new file mode 100755 index 00000000..529188b1 --- /dev/null +++ b/libparc/parc/algol/parc_EventTimer.c @@ -0,0 +1,128 @@ +/* + * 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 <LongBow/runtime.h> + +#include "internal_parc_Event.h" +#include <parc/algol/parc_EventTimer.h> + +static int _parc_event_timer_debug_enabled = 0; + +#define parcEventTimer_LogDebug(parcEventTimer, ...) \ + if (_parc_event_timer_debug_enabled) \ + parcLog_Debug(parcEventScheduler_GetLogger(parcEventTimer->eventScheduler), __VA_ARGS__) + +/** + * Current implementation based on top of libevent2 + */ +#include <event2/event.h> +#include <event2/util.h> + +struct PARCEventTimer { + /** + * The event instance. + */ + struct event *event; + + // Event scheduler we have been queued with + PARCEventScheduler *eventScheduler; + + PARCEventTimer_Callback *callback; + void *callbackUserData; +}; + +static void +_parc_event_timer_callback(evutil_socket_t fd, short flags, void *context) +{ + PARCEventTimer *parcEventTimer = (PARCEventTimer *) context; + parcEventTimer_LogDebug(parcEventTimer, + "_parc_event_timer_callback(fd=%x,flags=%x,parcEventTimer=%p)\n", + fd, flags, parcEventTimer); + parcEventTimer->callback((int) fd, internal_libevent_type_to_PARCEventType(flags), + parcEventTimer->callbackUserData); +} + +PARCEventTimer * +parcEventTimer_Create(PARCEventScheduler *eventScheduler, PARCEventType flags, PARCEvent_Callback *callback, void *callbackArgs) +{ + PARCEventTimer *parcEventTimer = parcMemory_Allocate(sizeof(PARCEventTimer)); + assertNotNull(parcEventTimer, "parcMemory_Allocate(%zu) returned NULL", sizeof(PARCEventTimer)); + + parcEventTimer->eventScheduler = eventScheduler; + parcEventTimer->callback = callback; + parcEventTimer->callbackUserData = callbackArgs; + + // NB: the EV_TIMEOUT flag is ignored when constructing an event + parcEventTimer->event = event_new(parcEventScheduler_GetEvBase(eventScheduler), -1, + internal_PARCEventType_to_libevent_type(flags), + _parc_event_timer_callback, parcEventTimer); + assertNotNull(parcEventTimer->event, "Could not create a new event!"); + + parcEventTimer_LogDebug(parcEventTimer, + "parcEventTimer_Create(base=%p,events=%x,cb=%p,args=%p) = %p\n", + parcEventScheduler_GetEvBase(eventScheduler), flags, + callback, callbackArgs, parcEventTimer); + + return parcEventTimer; +} + +int +parcEventTimer_Start(PARCEventTimer *parcEventTimer, struct timeval *timeout) +{ + parcEventTimer_LogDebug(parcEventTimer, + "parcEventTimer_Start(event=%p, timeout=%d:%d)\n", + parcEventTimer, timeout->tv_sec, timeout->tv_usec); + assertNotNull(parcEventTimer, "parcEventTimer_Start must be passed a valid event!"); + + int result = event_add(parcEventTimer->event, timeout); + return result; +} + +int +parcEventTimer_Stop(PARCEventTimer *parcEventTimer) +{ + parcEventTimer_LogDebug(parcEventTimer, "parcEventTimer_Stop(event=%p)\n", parcEventTimer); + assertNotNull(parcEventTimer, "parcEventTimer_Stop must be passed a valid event!"); + + int result = event_del(parcEventTimer->event); + return result; +} + +void +parcEventTimer_Destroy(PARCEventTimer **parcEventTimer) +{ + parcEventTimer_LogDebug((*parcEventTimer), "parcEventTimer_Destroy(parcEventTimer=%p)\n", *parcEventTimer); + assertNotNull(*parcEventTimer, "parcEventTimer_Destroy must be passed a valid parcEventTimer!"); + assertNotNull((*parcEventTimer)->event, "parcEventTimer_Destroy passed a null event!"); + + event_free((*parcEventTimer)->event); + parcMemory_Deallocate((void **) parcEventTimer); +} + +void +parcEventTimer_EnableDebug(void) +{ + _parc_event_timer_debug_enabled = 1; +} + +void +parcEventTimer_DisableDebug(void) +{ + _parc_event_timer_debug_enabled = 0; +} diff --git a/libparc/parc/algol/parc_EventTimer.h b/libparc/parc/algol/parc_EventTimer.h new file mode 100755 index 00000000..4a960853 --- /dev/null +++ b/libparc/parc/algol/parc_EventTimer.h @@ -0,0 +1,138 @@ +/* + * 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. + */ + +/** + * @file parc_EventTimer.h + * @ingroup events + * @brief Timer events + * + * Provides a facade implementing many regularly available event functions. + * This is an interface that software implementors may use to substitute + * different kinds of underlying implementations of these event management functions. + * Notable examples are libevent and libev. + * + */ +#ifndef libparc_parc_EventTimer_h +#define libparc_parc_EventTimer_h + +#include <parc/algol/parc_Event.h> + +typedef void (PARCEventTimer_Callback)(int fd, PARCEventType type, void *user_data); + +typedef struct PARCEventTimer PARCEventTimer; + +/** + * Create a new timer event instance. + * + * The new event instance is returned. + * + * @param [in] parcEventScheduler - scheduler instance to attach to + * @param [in] flags - event flags + * @param [in] callback - timer callback function + * @param [in] callbackArguments - private arguments passed to callback + * @returns A pointer to the a PARCEventTimer instance. + * + * Example: + * @code + * { + * PARCEventTimer *event = parcEvent_Create(parcEventScheduler, PARCEvent_None, callback, callbackArguments); + * } + * @endcode + * + */ +PARCEventTimer *parcEventTimer_Create(PARCEventScheduler *parcEventScheduler, + PARCEventType flags, + PARCEventTimer_Callback *callback, + void *callbackArguments); + +/** + * Position a timer event instance to be scheduled. + * + * @param [in] parcEventTimer - The newly created event instance and a timeout value. + * @param [in] timeout - time to wait for event, or NULL to wait forever. + * @returns -1 on error, 0 on success if nothing changed in the event backend, and 1 on success if something did. + * + * Example: + * @code + * addEvent(PARCEventTimer *parcEventTimer) + * { + * struct timeval timeout = {5, 0}; + * int result = parcEventTimer_Start(parcEventTimer, &timeout); + * } + * @endcode + * + */ +int parcEventTimer_Start(PARCEventTimer *parcEventTimer, struct timeval *timeout); + +/** + * Stop a timer event instance. + * + * @param [in] parcEventTimer - The newly created event instance and a timeout value. + * @returns 0 on success + * + * Example: + * @code + * removeEvent(PARCEventTimer *parcEventTimer) + * { + * parcEventTimer_Stop(parcEventTimer); + * } + * @endcode + * + */ +int parcEventTimer_Stop(PARCEventTimer *parcEventTimer); + +/** + * Destroy an event instance. + * + * The event instance is passed in. + * + * @param [in] parcEventTimer the instance to destroy. + * + * Example: + * @code + * { + * parcEventTimer_Destroy(&parcEventTimer); + * } + * @endcode + * + */ +void parcEventTimer_Destroy(PARCEventTimer **parcEventTimer); + +/** + * Turn on debugging flags and messages + * + * Example: + * @code + * { + * parcEventTimer_EnableDebug(); + * } + * @endcode + * + */ +void parcEventTimer_EnableDebug(void); + +/** + * Turn off debugging flags and messages + * + * Example: + * @code + * { + * parcEventTimer_DisableDebug(); + * } + * @endcode + * + */ +void parcEventTimer_DisableDebug(void); +#endif // libparc_parc_EventTimer_h diff --git a/libparc/parc/algol/parc_Execution.c b/libparc/parc/algol/parc_Execution.c new file mode 100644 index 00000000..8b31dcdb --- /dev/null +++ b/libparc/parc/algol/parc_Execution.c @@ -0,0 +1,97 @@ +// +// parc_Status.c +// Libparc +// +// +// +#include <config.h> +#include <stdio.h> + +#include "parc_Execution.h" + +/* + * A PARCExecution value is a unique thing which can have a string assigned to it. + * + * I want the function to be: + * + * A thing that returns an Execution + * + * Execution function() { } + * + * A thing that an Execution value can be + * + * Execution value = function; + */ + +struct PARCExecution { + struct PARCExecution (*type)(char *format, ...); + char *message; +}; + +PARCExecution *PARCExecution_OK = &(PARCExecution) { + .message = "OK" +}; + +PARCExecution *PARCExecution_Timeout = &(PARCExecution) { + .message = "Timeout" +}; + +PARCExecution *PARCExecution_Interrupted = &(PARCExecution) { + .message = "Interrupted" +}; + +PARCExecution *PARCExecution_IOError = &(PARCExecution) { + .message = "I/O Error" +}; + +PARCExecution * +parcExecution_OK(const char *format, ...) +{ + return PARCExecution_OK; +} + +PARCExecution * +parcExecution_Interrupted(const char *format, ...) +{ + return PARCExecution_Interrupted; +} + +PARCExecution * +parcExecution_IOError(const char *format, ...) +{ + return PARCExecution_IOError; +} + +bool +parcExecution_Is(const PARCExecution *exec, const PARCExecution *other) +{ + return (exec == other); +} + +char * +parcExecution_GetMessage(const PARCExecution *exec) +{ + return exec->message; +} + +PARCExecution * +bar() +{ + return PARCExecution_OK; +} + +PARCExecution * +baz() +{ + return parcExecution_OK("Nothing to say"); +} + +void +foo() +{ + PARCExecution *x = bar(); + PARCExecution *y = baz(); + + printf("%s\n", parcExecution_GetMessage(x)); + printf("%s\n", parcExecution_GetMessage(y)); +} diff --git a/libparc/parc/algol/parc_Execution.h b/libparc/parc/algol/parc_Execution.h new file mode 100644 index 00000000..1aadc589 --- /dev/null +++ b/libparc/parc/algol/parc_Execution.h @@ -0,0 +1,41 @@ +/* + * 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. + */ + +/** + * @file parc_Execution.h + * @ingroup datastructures + * @brief PARC Execution Status + * An extensible set of status values used to communicate + * out-of-band or exceptional conditions as return values. + * + */ +#ifndef parc_Execution_h +#define parc_Execution_h + +#include <stdbool.h> + +struct PARCExecution; +typedef struct PARCExecution PARCExecution; + +extern PARCExecution *PARCExecution_OK; +extern PARCExecution *PARCExecution_Interrupted; +extern PARCExecution *PARCExecution_IOError; +extern PARCExecution *PARCExecution_Timeout; + +char *parcExecution_GetMessage(const PARCExecution *exec); + +bool parcExecution_Is(const PARCExecution *exec, const PARCExecution *other); + +#endif /* parc_Status_h */ diff --git a/libparc/parc/algol/parc_File.c b/libparc/parc/algol/parc_File.c new file mode 100644 index 00000000..2ab307a2 --- /dev/null +++ b/libparc/parc/algol/parc_File.c @@ -0,0 +1,262 @@ +/* + * 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> + +#ifndef _XOPEN_SOURCE +#define _XOPEN_SOURCE 500 +#endif //_XOPEN_SOURCE 500 + +#ifndef __USE_XOPEN_EXTENDED +#define __USE_XOPEN_EXTENDED +#endif //__USE_XOPEN_EXTENDED + +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> + + +#include <ftw.h> +#include <inttypes.h> +#include <pthread.h> +#include <string.h> +#include <errno.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_PathName.h> +#include <parc/algol/parc_File.h> +#include <parc/algol/parc_Memory.h> + + +struct parc_file { + PARCPathName *pathName; +}; + +static bool +_parcFile_Destructor(PARCFile **filePtr) +{ + PARCFile *file = *filePtr; + + parcPathName_Release(&file->pathName); + + return true; +} + +parcObject_Override(PARCFile, PARCObject, + .destructor = (PARCObjectDestructor *) _parcFile_Destructor, + .toString = (PARCObjectToString *) parcFile_ToString); + +void +parcFile_AssertValid(const PARCFile *instance) +{ + trapIllegalValueIf(instance == NULL, "Parameter must be a non-null pointer to a valid PARCFile."); + trapIllegalValueIf(instance->pathName == NULL, "PARCFile cannot have a NULL path-name"); +} + +PARCFile * +parcFile_Create(const char *path) +{ + PARCFile *result = NULL; + + PARCPathName *pathName = parcPathName_Parse(path); + if (pathName != NULL) { + result = parcObject_CreateInstance(PARCFile); + if (result != NULL) { + result->pathName = pathName; + } else { + parcPathName_Release(&pathName); + } + } + + return result; +} + +PARCFile * +parcFile_CreateChild(const PARCFile *parent, char *fileName) +{ + PARCFile *result = NULL; + + PARCPathName *childPath = parcPathName_Append(parcPathName_Copy(parent->pathName), fileName); + + if (childPath != NULL) { + result = parcObject_CreateInstance(PARCFile); + if (result != NULL) { + result->pathName = childPath; + } + } + + return result; +} + +parcObject_ImplementAcquire(parcFile, PARCFile); + +parcObject_ImplementRelease(parcFile, PARCFile); + +bool +parcFile_CreateNewFile(const PARCFile *file) +{ + parcFile_OptionalAssertValid(file); + + bool result = false; + + char *string = parcPathName_ToString(file->pathName); + + int fd = open(string, O_EXCL | O_CREAT | O_TRUNC, 0666); + if (fd != -1) { + close(fd); + result = true; + } + parcMemory_Deallocate((void **) &string); + + return result; +} + +bool +parcFile_Mkdir(const PARCFile *file) +{ + parcFile_OptionalAssertValid(file); + + char *string = parcPathName_ToString(file->pathName); + bool result = (mkdir(string, 0777) == 0); + parcMemory_Deallocate((void **) &string); + + return result; +} + +bool +parcFile_Exists(const PARCFile *file) +{ + struct stat statbuf; + + char *string = parcPathName_ToString(file->pathName); + bool result = stat(string, &statbuf) == 0; + parcMemory_Deallocate((void **) &string); + + return result; +} + +bool +parcFile_IsDirectory(const PARCFile *file) +{ + bool result = false; + struct stat statbuf; + + char *string = parcPathName_ToString(file->pathName); + if (stat(string, &statbuf) == 0) { + result = S_ISDIR(statbuf.st_mode); + } + parcMemory_Deallocate((void **) &string); + + return result; +} + +static int +_deleteNode(const char *path, const struct stat *stat, int flag, struct FTW *ftwbuf) +{ + int result = 0; + + if (flag == FTW_DP) { // directory in post-order + result = rmdir(path); + } else { + result = unlink(path); + } + return result; +} + +/** + * @function parcFile_Delete + * @abstract Deletes the file or directory + * @discussion + * + * @param <#param1#> + * @return true if everything deleted, false otherwise + */ +bool +parcFile_Delete(const PARCFile *file) +{ + char *string = parcPathName_ToString(file->pathName); + + // only allow under tmp + assertTrue(strncmp(string, "/tmp/", 5) == 0, + "Path must begin with /tmp/: %s", string); + // dont allow ".." + assertNull(strstr(string, ".."), "Path cannot have .. in it: %s", string); + + bool result = false; + if (parcFile_IsDirectory(file)) { + // depth first, dont't follow symlinks, do not cross mount points. + int flags = FTW_DEPTH | FTW_PHYS | FTW_MOUNT; + + // maximum 20 fds open at a time + int maximumFileDescriptors = 20; + + int failure = nftw(string, _deleteNode, maximumFileDescriptors, flags); + assertFalse(failure, "Error on recursive delete: (%d) %s", errno, strerror(errno)); + + result = failure == false; + } else { + result = (unlink(string) == 0); + } + + parcMemory_Deallocate((void **) &string); + + return result; +} + +PARCBufferComposer * +parcFile_BuildString(const PARCFile *file, PARCBufferComposer *composer) +{ + parcPathName_BuildString(file->pathName, composer); + + return composer; +} + +char * +parcFile_ToString(const PARCFile *file) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + parcFile_BuildString(file, composer); + + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + char *result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + + parcBufferComposer_Release(&composer); + + return result; +} + +size_t +parcFile_GetFileSize(const PARCFile *file) +{ + size_t fileSize = 0; + + char *fname = parcPathName_ToString(file->pathName); + + struct stat st; + + if (stat(fname, &st) == 0) { + fileSize = st.st_size; + } + + parcMemory_Deallocate(&fname); + + return fileSize; +} diff --git a/libparc/parc/algol/parc_File.h b/libparc/parc/algol/parc_File.h new file mode 100755 index 00000000..42ef232e --- /dev/null +++ b/libparc/parc/algol/parc_File.h @@ -0,0 +1,278 @@ +/* + * 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. + */ + +/** + * @file parc_File.h + * @ingroup inputoutput + * @brief File manipulation + * + * + */ +#ifndef libparc_parc_File_h +#define libparc_parc_File_h + +#include <stdbool.h> + +#include <parc/algol/parc_BufferComposer.h> + +#ifdef PARCLibrary_DISABLE_VALIDATION +# define parcFile_OptionalAssertValid(_instance_) +#else +# define parcFile_OptionalAssertValid(_instance_) parcFile_AssertValid(_instance_) +#endif + +struct parc_file; +typedef struct parc_file PARCFile; + +/** + * Creates a `PARCFile` object named by pathname. + * + * This operation does not imply any I/O operations. + * The PARCFile instance only represents the pathname, + * and does not necessarily reference a real file. + * + * @param [in] pathname is a pointer to a char array (string) + * @return A pointer to an instance of `PARCFile` + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCFile *parcFile_Create(const char *pathname); + +/** + * Acquire a new reference to an instance of `PARCFile`. + * + * The reference count to the instance is incremented. + * + * @param [in] file The instance of `PARCFile` to which to refer. + * + * @return The same value as the input parameter @p file + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +PARCFile *parcFile_Acquire(const PARCFile *file); + +/** + * Assert that an instance of `PARCFile` is valid. + * + * Valid means the internal state of the type is consistent with its required current or future behaviour. + * This may include the validation of internal instances of types. + * + * @param [in] instance A pointer to a `PARCFile` instance. + * + * Example: + * @code + * { + * PARCFile *file = parcFile_Create("/tmp/foo"); + * + * parcFile_AssertValid(file); + * } + * @endcode + */ +void parcFile_AssertValid(const PARCFile *instance); + +/** + * Release a `PARCFile` reference. + * + * Only the last invocation where the reference count is decremented to zero, + * will actually destroy the `PARCFile`. + * + * @param [in,out] filePtr is a pointer to the `PARCFile` reference. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void parcFile_Release(PARCFile **filePtr); + +/** + * Create a new file on storage. + * + * @param [in] file A pointer to an instance of `PARCFile` + * + * @return true if succesful, false if not + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +bool parcFile_CreateNewFile(const PARCFile *file); + +/** + * Return true if the PARCFile exists on storage. + * + * If the pathname can be stat(2)'d, then it exists. + * + * @param [in] file A pointer to a `PARCFile` instance. + * @return true if the file exists, false otherwise + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool parcFile_Exists(const PARCFile *file); + +/** + * Create a new directory. + * + * @param [in] file A pointer to a `PARCFile` instance. + * @return true if the pathname exists, false otherwise + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +bool parcFile_Mkdir(const PARCFile *file); + +/** + * True if the specified `PARCFile` is an existing directory on storage. + * + * @param [in] file A pointer to a `PARCFile` instance. + * + * @return true if specified `PARCFile` is an existing directory on storage, false if not + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +bool parcFile_IsDirectory(const PARCFile *file); + +/** + * Deletes the file or directory on storage. + * + * For a directory, it does a recursive delete. + * + * @param [in] file The instance of `PARCFile` to be deleted. + * @return `true` if everything deleted, false otherwise + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool parcFile_Delete(const PARCFile *file); + +/** + * Append a representation of the specified `PARCFile` instance to the given {@link PARCBufferComposer}. + * + * @param [in] file A pointer to the `PARCFile` instance whose contents should be appended to to string. + * @param [in,out] string A pointer to the `PARCBufferComposer` instance to which the contents of file will be appended. + * + * @return NULL Cannot allocate memory. + * @return non-NULL The instance of `PARCBufferComposer` with the appended contents. + * + * Example: + * @code + * { + * PARCBufferComposer *result = parcBufferComposer_Create(); + * PARCFile *instance = parcFile_Create("/tmp/foo"); + * + * parcFile_BuildString(instance, result); + * + * PARCBuffer *string = parcBufferComposer_FinalizeBuffer(result); + * printf("File: %s\n", parcBuffer_ToString(string)); + * parcBuffer_Release(&string); + * + * parcBufferComposer_Release(&result); + * } + * @endcode + */ +PARCBufferComposer *parcFile_BuildString(const PARCFile *file, PARCBufferComposer *string); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in#> | <#out#> | <#in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +size_t parcFile_GetFileSize(const PARCFile *file); + +/** + * Create a PARCFile representation of the home directory of the current user. + * + * The return value must be released via `parcFile_Release`. + * + * @return non-NULL A pointer to a valid PARCFile instance. + * @return NULL An error occurred. + * + * Example: + * @code + * { + * PARCFile *directory = parcFile_CreateHome(); + * } + * @endcode + */ +PARCFile *parcFile_CreateHome(void); + +/** + * Produce a null-terminated string representation of the specified `PARCFile` instance. + * + * The non-null result must be freed by the caller via {@link parcMemory_Deallocate}. + * + * @param [in] file A pointer to the `PARCFile` instance. + * + * @return NULL Cannot allocate memory. + * @return non-NULL A pointer to an allocated, null-terminated C string that must be deallocated via {@link parcMemory_Deallocate}. + * + * Example: + * @code + * { + * PARCFile *instance = parcFile_Create("/tmp/foo"); + * + * char *string = parcFile_ToString(instance); + * + * if (string != NULL) { + * printf("Hello: %s\n", string); + * parcMemory_Deallocate((void **)&string); + * } else { + * printf("Cannot allocate memory\n"); + * } + * + * parcFile_Release(&instance); + * } + * @endcode + * + * @see {@link parcFile_BuildString} + */ +char *parcFile_ToString(const PARCFile *file); +#endif // libparc_parc_File_h diff --git a/libparc/parc/algol/parc_FileChunker.c b/libparc/parc/algol/parc_FileChunker.c new file mode 100644 index 00000000..d7acd58e --- /dev/null +++ b/libparc/parc/algol/parc_FileChunker.c @@ -0,0 +1,282 @@ +/* + * 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 <stdio.h> +#include <string.h> +#include <sys/time.h> + +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_Buffer.h> +#include <parc/algol/parc_Memory.h> + +#include <parc/algol/parc_RandomAccessFile.h> + +#include <parc/algol/parc_FileChunker.h> + +PARCChunkerInterface *PARCFileChunkerAsChunker = &(PARCChunkerInterface) { + .ForwardIterator = (void *(*)(const void *))parcFileChunker_ForwardIterator, + .ReverseIterator = (void *(*)(const void *))parcFileChunker_ReverseIterator, + .GetChunkSize = (size_t (*)(const void *))parcFileChunker_GetChunkSize +}; + +struct _parc_chunker_state { + size_t chunkNumber; + int direction; + bool atEnd; + size_t position; + size_t nextChunkSize; + size_t totalSize; +}; + +typedef struct _parc_chunker_state _ChunkerState; + +struct parc_buffer_chunker { + // State + size_t chunkSize; + + // Container for the data to be parsed + PARCFile *file; + PARCRandomAccessFile *fhandle; + + // The current element of the iterator + PARCBuffer *currentElement; +}; + +static void +_destroy(PARCFileChunker **chunkerP) +{ + if ((*chunkerP)->fhandle != NULL) { + parcRandomAccessFile_Release(&(*chunkerP)->fhandle); + } + + if ((*chunkerP)->file != NULL) { + parcFile_Release(&(*chunkerP)->file); + } + + if ((*chunkerP)->currentElement != NULL) { + parcBuffer_Release(&(*chunkerP)->currentElement); + } +} + +static void * +_InitForward(PARCFileChunker *chunker) +{ + _ChunkerState *state = parcMemory_Allocate(sizeof(_ChunkerState)); + + state->chunkNumber = 0; + state->direction = 0; + state->position = 0; + state->atEnd = false; + state->totalSize = parcFile_GetFileSize(chunker->file); + + if (state->totalSize < chunker->chunkSize) { + state->position = 0; + state->nextChunkSize = state->totalSize; + } else { + state->position = 0; + state->nextChunkSize = chunker->chunkSize; + } + + return state; +} + +static void * +_InitReverse(PARCFileChunker *chunker) +{ + _ChunkerState *state = parcMemory_Allocate(sizeof(_ChunkerState)); + + state->chunkNumber = 0; + state->direction = 1; + state->atEnd = false; + state->totalSize = parcFile_GetFileSize(chunker->file); + + if (state->totalSize < chunker->chunkSize) { + state->position = 0; + state->nextChunkSize = state->totalSize; + } else { + state->position = state->totalSize - chunker->chunkSize; + state->nextChunkSize = chunker->chunkSize; + } + + return state; +} + +static bool +_parcChunker_HasNext(PARCFileChunker *chunker, void *voidstate) +{ + _ChunkerState *state = (_ChunkerState *) voidstate; + return !state->atEnd; +} + +static void +_advanceStateForward(PARCFileChunker *chunker, _ChunkerState *state) +{ + state->position += state->nextChunkSize; + size_t remaining = state->totalSize - state->position; + + if (remaining == 0) { + state->atEnd = true; + } else if (remaining > chunker->chunkSize) { + state->nextChunkSize = chunker->chunkSize; + } else { + state->nextChunkSize = remaining; + } +} + +static void +_advanceStateBackward(PARCFileChunker *chunker, _ChunkerState *state) +{ + // Previous chunk size + size_t chunkSize = state->nextChunkSize; + if (chunkSize != chunker->chunkSize || state->position == 0) { + state->atEnd = true; + } else { + if (state->position < chunkSize) { + state->nextChunkSize = state->position; // on next read, go to the current position + state->position = 0; // we reached the beginning + } else { + state->position -= chunkSize; + } + } +} + +static void +_advanceState(PARCFileChunker *chunker, _ChunkerState *state) +{ + state->chunkNumber++; + + if (state->direction == 0) { + _advanceStateForward(chunker, state); + } else { + _advanceStateBackward(chunker, state); + } +} + +static void * +_parcChunker_NextFromBuffer(PARCFileChunker *chunker, _ChunkerState *state) +{ + size_t chunkSize = state->nextChunkSize; + + parcRandomAccessFile_Seek(chunker->fhandle, state->position, PARCRandomAccessFilePosition_Start); + + PARCBuffer *slice = parcBuffer_Allocate(chunkSize); + parcRandomAccessFile_Read(chunker->fhandle, slice); + slice = parcBuffer_Flip(slice); + + _advanceState(chunker, state); + + return slice; +} + +static void * +_parcChunker_Next(PARCFileChunker *chunker, void *state) +{ + PARCBuffer *buffer = _parcChunker_NextFromBuffer(chunker, state); + + if (chunker->currentElement != NULL) { + parcBuffer_Release(&chunker->currentElement); + } + if (buffer != NULL) { + chunker->currentElement = parcBuffer_Acquire(buffer); + } + + return state; +} + +static void +_parcChunker_RemoveAt(PARCFileChunker *chunker, void **state) +{ + // pass +} + +static void * +_parcChunker_GetElement(PARCFileChunker *chunker, void *state) +{ + return chunker->currentElement; +} + +static void +_parcChunker_Finish(PARCFileChunker *chunker, void *state) +{ + _ChunkerState *thestate = (_ChunkerState *) state; + parcMemory_Deallocate(&thestate); +} + +static void +_parcChunker_AssertValid(const void *state) +{ + // pass +} + +parcObject_ExtendPARCObject(PARCFileChunker, _destroy, NULL, NULL, NULL, NULL, NULL, NULL); +parcObject_ImplementAcquire(parcFileChunker, PARCFileChunker); +parcObject_ImplementRelease(parcFileChunker, PARCFileChunker); + +PARCFileChunker * +parcFileChunker_Create(PARCFile *file, size_t chunkSize) +{ + PARCFileChunker *chunker = parcObject_CreateInstance(PARCFileChunker); + + if (chunker != NULL) { + chunker->chunkSize = chunkSize; + chunker->file = parcFile_Acquire(file); + chunker->fhandle = parcRandomAccessFile_Open(chunker->file); + chunker->currentElement = NULL; + } + + return chunker; +} + + +PARCIterator * +parcFileChunker_ForwardIterator(const PARCFileChunker *chunker) +{ + PARCIterator *iterator = parcIterator_Create((void *) chunker, + (void *(*)(PARCObject *))_InitForward, + (bool (*)(PARCObject *, void *))_parcChunker_HasNext, + (void *(*)(PARCObject *, void *))_parcChunker_Next, + (void (*)(PARCObject *, void **))_parcChunker_RemoveAt, + (void *(*)(PARCObject *, void *))_parcChunker_GetElement, + (void (*)(PARCObject *, void *))_parcChunker_Finish, + (void (*)(const void *))_parcChunker_AssertValid); + + return iterator; +} + +PARCIterator * +parcFileChunker_ReverseIterator(const PARCFileChunker *chunker) +{ + PARCIterator *iterator = parcIterator_Create((void *) chunker, + (void *(*)(PARCObject *))_InitReverse, + (bool (*)(PARCObject *, void *))_parcChunker_HasNext, + (void *(*)(PARCObject *, void *))_parcChunker_Next, + (void (*)(PARCObject *, void **))_parcChunker_RemoveAt, + (void *(*)(PARCObject *, void *))_parcChunker_GetElement, + (void (*)(PARCObject *, void *))_parcChunker_Finish, + (void (*)(const void *))_parcChunker_AssertValid); + + return iterator; +} + +size_t +parcFileChunker_GetChunkSize(const PARCFileChunker *chunker) +{ + return chunker->chunkSize; +} diff --git a/libparc/parc/algol/parc_FileChunker.h b/libparc/parc/algol/parc_FileChunker.h new file mode 100755 index 00000000..6ab0d267 --- /dev/null +++ b/libparc/parc/algol/parc_FileChunker.h @@ -0,0 +1,219 @@ +/* + * 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. + */ + +/** + * @file parc_FileChunker.h + * @ingroup ContentObject + * @brief A FileChunker is a chunker that segments the content of a file. + * + */ + +#ifndef libparc_parc_FileChunker_h +#define libparc_parc_FileChunker_h + +#include <parc/algol/parc_Chunker.h> + +#include <parc/algol/parc_File.h> + +struct parc_buffer_chunker; +/** + * @typedef PARCFileChunker + * @brief The PARC Chunker + */ +typedef struct parc_buffer_chunker PARCFileChunker; + +/** + * The mapping of a `PARCFileChunker` to the generic `PARCChunker`. + */ +extern PARCChunkerInterface *PARCFileChunkerAsChunker; + +/** + * Create a new chunker to segment data contained in a `PARCBuffer.` + * + * @param [in] file A `PARCFile` from which the data will be read. + * @param [in] chunkSize The size per chunk. + * + * @retval PARCFileChunker A newly allocated `PARCFileChunker` + * @retval NULL An error occurred. + * + * Example + * @code + * { + * PARCBuffer *dataToChunk = ... + * PARCFileChunker *chunker = PARCFileChunker_CreateFromBuffer(dataToChunk, 32); + * } + */ +PARCFileChunker *parcFileChunker_Create(PARCFile *file, size_t chunkSize); + +/** + * Increase the number of references to a `PARCFileChunker` instance. + * + * Note that new `PARCFileChunker` is not created, + * only that the given `PARCFileChunker` reference count is incremented. + * Discard the reference by invoking {@link PARCFileChunker_Release}. + * + * @param [in] chunker A pointer to the original `PARCFileChunker`. + * @return The value of the input parameter @p chunker. + * + * Example: + * @code + * { + * PARCFileChunker *original = parcFileChunker_Create(...); + * + * PARCFileChunker *reference = parcFileChunker_Acquire(original); + * + * parcFileChunker_Release(&original); + * parcFileChunker_Release(&reference); + * } + * @endcode + * + * @see parcFileChunker_Release + */ +PARCFileChunker *parcFileChunker_Acquire(const PARCFileChunker *chunker); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] chunkerP A pointer to a pointer to the instance to release. + * + * + * Example: + * @code + * { + * ... + * + * PARCFileChunker *chunker = parcFileChunker_Acquire(instance); + * + * parcFileChunker_Release(&chunker); + * } + * @endcode + */ +void parcFileChunker_Release(PARCFileChunker **chunkerP); + +/** + * Determine if two `PARCFileChunker` instances are equal. + * + * The following equivalence relations on non-null `PARCFileChunker` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, `parcFileChunker_Equals(x, x)` + * must return true. + * + * * It is symmetric: for any non-null reference values x and y, + * `parcFileChunker_Equals(x, y)` must return true if and only if + * `parcFileChunker_Equals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcFileChunker_Equals(x, y)` returns true and + * `parcFileChunker_Equals(y, z)` returns true, + * then `parcFileChunker_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple + * invocations of `parcFileChunker_Equals(x, y)` consistently return true or + * consistently return false. + * + * * For any non-null reference value x, `parcFileChunker_Equals(x, NULL)` must + * return false. + * + * @param chunkerA A pointer to a `PARCFileChunker` instance. + * @param chunkerB A pointer to a `PARCFileChunker` instance. + * @return true if the two `PARCFileChunker` instances are equal. + * + * Example: + * @code + * { + * char *fileToChunk = ... + * PARCFileChunker *chunkerA = parcFileChunker_Create(fileToChunk, 32); + * PARCFileChunker *chunkerB = parcFileChunker_Create(fileToChunk, 32); + * + * bool equals = parcFileChunker_Equals(chunkerA, chunkerB); + * } + * @endcode + */ +bool parcFileChunker_Equals(const PARCFileChunker *chunkerA, const PARCFileChunker *chunkerB); + +/** + * Return an iterator to traverse the chunks of the underlying data in sequential order. + * + * This function can only be called once per chunker instance since the iterator + * will mutate internal state of the chunker. + * + * @param [in] chunker A `PARCFileChunker` instance. + * + * @return a `PARCIterator` that traverses the chunks of the underlying data. + * + * Example + * @code + * { + * char *fileToChunk = ... + * PARCFileChunker *chunker = parcFileChunker_Create(fileToChunk, 32); + * + * PARCIterator *itr = parcFileChunker_ForwardIterator(chunker); + * + * // use the iterator to traverse the chunker + * } + * @endcode + */ +PARCIterator *parcFileChunker_ForwardIterator(const PARCFileChunker *chunker); + +/** + * Return an iterator to traverse the chunks of the underlying data in sequential order. + * + * This function can only be called once per chunker instance since the iterator + * will mutate internal state of the chunker. + * + * @param [in] chunker A `PARCFileChunker` instance. + * + * @return a `PARCIterator` that traverses the chunks of the underlying data. + * + * Example + * @code + * { + * char *fileToChunk = ... + * PARCFileChunker *chunker = parcFileChunker_Create(fileToChunk, 32); + * + * PARCIterator *itr = parcFileChunker_ReverseIterator(chunker); + * + * // use the iterator to traverse the chunker + * } + * @endcode + */ +PARCIterator *parcFileChunker_ReverseIterator(const PARCFileChunker *chunker); + +/** + * Get the chunk size of a `PARCFileChunker.` + * + * @param [in] chunker A `PARCFileChunker` instance. + * + * @return the chunk size + * + * Example + * @code + * { + * PARCBuffer *dataToChunk = ... + * PARCFileChunker *chunker = ... + * + * size_t chunkSize = parcFileChunker_GetChunkSize(chunker); + * } + * @endcode + */ +size_t parcFileChunker_GetChunkSize(const PARCFileChunker *chunker); +#endif // libparc_parc_FileChunker_h diff --git a/libparc/parc/algol/parc_FileInputStream.c b/libparc/parc/algol/parc_FileInputStream.c new file mode 100755 index 00000000..f42ced73 --- /dev/null +++ b/libparc/parc/algol/parc_FileInputStream.c @@ -0,0 +1,115 @@ +/* + * 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 <fcntl.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/uio.h> +#include <unistd.h> +#include <sys/stat.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_File.h> +#include <parc/algol/parc_Buffer.h> +#include <parc/algol/parc_InputStream.h> +#include <parc/algol/parc_FileInputStream.h> +#include <parc/algol/parc_Object.h> + +PARCInputStreamInterface *PARCFileInputStreamAsPARCInputStream = &(PARCInputStreamInterface) { + .Acquire = (PARCInputStream * (*)(const PARCInputStream *))parcFileInputStream_Acquire, + .Release = (void (*)(PARCInputStream **))parcFileInputStream_Release, + .Read = (size_t (*)(PARCInputStream *, PARCBuffer *))parcFileInputStream_Read +}; + +struct parc_file_input_stream { + int fd; +}; + +static void +_destroy(PARCFileInputStream **inputStreamPtr) +{ + PARCFileInputStream *inputStream = *inputStreamPtr; + + close(inputStream->fd); +} + +parcObject_ExtendPARCObject(PARCFileInputStream, _destroy, NULL, NULL, NULL, NULL, NULL, NULL); + +PARCFileInputStream * +parcFileInputStream_Open(const PARCFile *file) +{ + PARCFileInputStream *result = NULL; + + char *fileName = parcFile_ToString(file); + if (fileName != NULL) { + result = parcFileInputStream_Create(open(fileName, O_RDONLY)); + parcMemory_Deallocate((void **) &fileName); + } + + return result; +} + +PARCFileInputStream * +parcFileInputStream_Create(int fileDescriptor) +{ + trapIllegalValueIf(fileDescriptor < 0, "File descriptor must not be negative."); + + PARCFileInputStream *result = parcObject_CreateInstance(PARCFileInputStream); + if (result != NULL) { + result->fd = fileDescriptor; + } + + return result; +} + +parcObject_ImplementAcquire(parcFileInputStream, PARCFileInputStream); +parcObject_ImplementRelease(parcFileInputStream, PARCFileInputStream); + +bool +parcFileInputStream_Read(PARCFileInputStream *inputStream, PARCBuffer *buffer) +{ + while (parcBuffer_HasRemaining(buffer)) { + void *buf = parcBuffer_Overlay(buffer, 0); + ssize_t nread = read(inputStream->fd, buf, parcBuffer_Remaining(buffer)); + if (nread < 0) { + break; + } + parcBuffer_SetPosition(buffer, parcBuffer_Position(buffer) + nread); + } + return parcBuffer_HasRemaining(buffer); +} + +PARCBuffer * +parcFileInputStream_ReadFile(PARCFileInputStream *inputStream) +{ + PARCBuffer *result = NULL; + + struct stat statbuf; + + if (fstat(inputStream->fd, &statbuf) == 0) { + result = parcBuffer_Allocate(statbuf.st_size); + if (result != NULL) { + parcFileInputStream_Read(inputStream, result); + } + } + + return result; +} diff --git a/libparc/parc/algol/parc_FileInputStream.h b/libparc/parc/algol/parc_FileInputStream.h new file mode 100755 index 00000000..3641e08a --- /dev/null +++ b/libparc/parc/algol/parc_FileInputStream.h @@ -0,0 +1,170 @@ +/* + * 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. + */ + +/** + * @file parc_FileInputStream.h + * @ingroup inputoutput + * @brief A FileInputStream obtains input bytes from a file in a file system. + * + * What files are available depends on the host environment. + * FileInputStream is meant for reading streams of raw bytes such as image data. + * + */ + +#ifndef libparc_parc_FileInputStream_h +#define libparc_parc_FileInputStream_h + +#include <parc/algol/parc_File.h> +#include <parc/algol/parc_Buffer.h> +#include <parc/algol/parc_InputStream.h> + +struct parc_file_input_stream; + +/** + * @typedef PARCFileInputStream + * @brief Read streams of input + */ + +typedef struct parc_file_input_stream PARCFileInputStream; + +/** + * The mapping of a `PARCFileInputStream` to the generic `PARCInputStream`. + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCInputStreamInterface *PARCFileInputStreamAsPARCInputStream; + +/** + * Create a `PARCFileInputStream` instance. + * + * @param [in] fileDescriptor An abstract indicator for accessing a specific file + * + * @return non-NULL A pointer to an instance of `PARCFileInputStream` + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +PARCFileInputStream *parcFileInputStream_Create(int fileDescriptor); + +/** + * Create a `PARCFileInputStream` instance by opening an existing {@link PARCFile} instance. + * + * The file specified by `PARCFile` must exist and readable. + * + * @param [in] file A pointer to a `PARCFile` instance representing the existing file. + * + * @return non-NULL A pointer to a `PARCFileInputStream` instance. + * @return NULL Memory could not be allocated. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +PARCFileInputStream *parcFileInputStream_Open(const PARCFile *file); + +/** + * Acquire a new reference to an instance of `PARCFileInputStream`. + * + * The reference count to the instance is incremented. + * + * @param [in] inputStream The instance of `PARCFileInputStream` to which to refer. + * + * @return The same value as the input parameter @p inputStream + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +PARCFileInputStream *parcFileInputStream_Acquire(const PARCFileInputStream *inputStream); + +/** + * Release a `PARCFileInputStream` reference. + * + * Only the last invocation where the reference count is decremented to zero, + * will actually destroy the `PARCFileInputStream`. + * + * @param [in,out] inputStreamPtr is a pointer to the `PARCFileInputStream` reference. + * + * Example: + * @code + * <#example#> + * @endcode + */ + +void parcFileInputStream_Release(PARCFileInputStream **inputStreamPtr); + +/** + * Read a `PARCFileInputStream` into a {@link PARCBuffer}. + * + * The contents of the `PARCBuffer` are filled from the current position to the limit. + * When this function returns, the position is set to the end of the last successfully read byte of data. + * + * @param [in] inputStream The `PARCInputStream` to read. + * @param [in] buffer The `PARCBuffer` to read, from the current position of the buffer to its limit. + * + * @return true The entire contents of the `PARCBuffer`, from the current position to the limit, were filled. + * @return false The entire contents of the `PARCBuffer` were not filled. + * + * Example: + * @code + * { + * PARCFileOutputStream *stream = + * parcFileOutputStream_Create(open("/tmp/test_parc_FileOutputStream", O_CREAT|O_WRONLY|O_TRUNC, 0600)); + * + * PARCBuffer *buffer = parcBuffer_Allocate(16 * 1024*1024); + * + * parcFileOutputStream_Write(stream, buffer); + * + * assertFalse(parcBuffer_HasRemaining(buffer), "Expected the buffer to be emtpy"); + * + * parcBuffer_Release(&buffer); + * + * parcFileOutputStream_Release(&stream); + * } + * @endcode + * + */ +bool parcFileInputStream_Read(PARCFileInputStream *inputStream, PARCBuffer *buffer); + +/** + * Read the content of a `PARCFileInputStream` into a {@link PARCBuffer}. + * + * @param [in] inputStream The `PARCFileInputStream` to read. + * + * @return non-NULL A pointer to a `PARCBuffer` instance containing the content of the `PARCFileInputStream`. + * @return NULl Memory could not be allocated. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +PARCBuffer *parcFileInputStream_ReadFile(PARCFileInputStream *inputStream); +#endif // libparc_parc_FileInputStream_h diff --git a/libparc/parc/algol/parc_FileOutputStream.c b/libparc/parc/algol/parc_FileOutputStream.c new file mode 100755 index 00000000..a17dc66c --- /dev/null +++ b/libparc/parc/algol/parc_FileOutputStream.c @@ -0,0 +1,85 @@ +/* + * 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 <stdio.h> +#include <unistd.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_FileOutputStream.h> +#include <parc/algol/parc_Object.h> + +PARCOutputStreamInterface *PARCFileOutputStreamAsPARCInputStream = &(PARCOutputStreamInterface) { + .Acquire = (PARCOutputStream * (*)(PARCOutputStream *))parcFileOutputStream_Acquire, + .Release = (void (*)(PARCOutputStream **))parcFileOutputStream_Release, + .Write = (size_t (*)(PARCOutputStream *, PARCBuffer *))parcFileOutputStream_Write +}; + +struct parc_file_output_stream { + int fd; +}; + +static void +_destroy(PARCFileOutputStream **streamPtr) +{ + PARCFileOutputStream *stream = *streamPtr; + + close(stream->fd); +} + +parcObject_ExtendPARCObject(PARCFileOutputStream, _destroy, NULL, NULL, NULL, NULL, NULL, NULL); + +PARCFileOutputStream * +parcFileOutputStream_Create(int fileDescriptor) +{ + assertTrue(fileDescriptor != -1, "Invalid file descriptor"); + + PARCFileOutputStream *result = parcObject_CreateInstance(PARCFileOutputStream); + result->fd = fileDescriptor; + + return result; +} + +PARCOutputStream * +parcFileOutputStream_AsOutputStream(PARCFileOutputStream *fileOutputStream) +{ + return parcOutputStream_Create(parcFileOutputStream_Acquire(fileOutputStream), PARCFileOutputStreamAsPARCInputStream); +} + +parcObject_ImplementAcquire(parcFileOutputStream, PARCFileOutputStream); + +parcObject_ImplementRelease(parcFileOutputStream, PARCFileOutputStream); + +bool +parcFileOutputStream_Write(PARCFileOutputStream *outputStream, PARCBuffer *buffer) +{ + const size_t maximumChunkSize = 1024 * 1024; + + while (parcBuffer_HasRemaining(buffer)) { + size_t remaining = parcBuffer_Remaining(buffer); + size_t chunkSize = remaining > maximumChunkSize ? maximumChunkSize : remaining; + void *buf = parcBuffer_Overlay(buffer, chunkSize); + ssize_t nwritten = write(outputStream->fd, buf, chunkSize); + if (nwritten == -1) { + break; + } + } + + return parcBuffer_HasRemaining(buffer) == false; +} diff --git a/libparc/parc/algol/parc_FileOutputStream.h b/libparc/parc/algol/parc_FileOutputStream.h new file mode 100644 index 00000000..51251561 --- /dev/null +++ b/libparc/parc/algol/parc_FileOutputStream.h @@ -0,0 +1,142 @@ +/* + * 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. + */ + +/** + * @file parc_FileOutputStream.h + * @ingroup inputoutput + * @brief A file output stream is an output stream for writing data to a File or to a FileDescriptor. + * + * Whether or not a file is available or may be created depends upon the underlying platform. + * Some platforms, in particular, allow a file to be opened for writing by only one FileOutputStream + * (or other file-writing object) at a time. In such situations the constructors in this class will + * fail if the file involved is already open. + * + */ + +#ifndef libparc_parc_FileOutputStream_h +#define libparc_parc_FileOutputStream_h + +#include <parc/algol/parc_Buffer.h> +#include <parc/algol/parc_OutputStream.h> + +struct parc_file_output_stream; +typedef struct parc_file_output_stream PARCFileOutputStream; + +/** + * The mapping of a `PARCFileOutputStream` to the generic `PARCInputStream`. + * + */ +extern PARCOutputStreamInterface *PARCFileOutputStreamAsPARCOutputStream; + +/** + * Create a new output stream on a file descriptor. + * + * Caution: When the resulting `PARCFileOutputStream` is released, the file descriptor is closed. + * If you wrap STDOUT_FILENO, for example, the standard output of the application will be closed + * when this PARCFileOutputStream is released. + * To avoid this, use dup(2) or dup2(2). + * + * @param [in] fileDescriptor The fileDescriptor for the file on which to create an output stream. + * + * @return A pointer to a new instance of `PARCFileOutputStream` + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +PARCFileOutputStream *parcFileOutputStream_Create(int fileDescriptor); + +/** + * Convert an instance of `PARCFileOutputStream` to a `PARCOutputStream`. + * + * @param [in] fileOutputStream A pointer to a valid PARCFileOutputStream. + * + * @return A pointer to a new instance of `PARCOutputStream`. + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCOutputStream *parcFileOutputStream_AsOutputStream(PARCFileOutputStream *fileOutputStream); + +/** + * Acquire a new reference to an instance of `PARCFileOutputStream`. + * + * The reference count to the instance is incremented. + * + * @param [in] stream The instance of `PARCFileOutputStream` to which to refer. + * + * @return The same value as the input parameter @p stream + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +PARCFileOutputStream *parcFileOutputStream_Acquire(const PARCFileOutputStream *stream); + +/** + * Release a `PARCFileOutputStream` reference. + * + * Only the last invocation where the reference count is decremented to zero, + * will actually destroy the `PARCFileOutputStream`. + * + * @param [in,out] streamPtr is a pointer to the `PARCFileOutputStream` reference. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void parcFileOutputStream_Release(PARCFileOutputStream **streamPtr); + +/** + * Write the contents of a {@link PARCBuffer} to the given `PARCFileOutputStream`. + * + * The contents of the `PARCBuffer` from the current position to the limit are written to the `PARCFileOutputStream`. + * When this function returns the position is set to the end of the last successfully written byte of data. + * + * @param [in,out] outputStream The `PARCOutputStream` to write to. + * @param [in] buffer The `PARCBuffer` to write, from the current position of the buffer to its limit. + * + * @return true The entire contents of the `PARCBuffer` were written. + * @return false The entire contents of the `PARCBuffer` were not written. + * + * Example: + * @code + * { + * PARCFileOutputStream *stream = + * parcFileOutputStream_Create(open("/tmp/test_parc_FileOutputStream", O_CREAT|O_WRONLY|O_TRUNC, 0600)); + * + * PARCBuffer *buffer = parcBuffer_Allocate(16 * 1024*1024); + * + * parcFileOutputStream_Write(stream, buffer); + * + * assertFalse(parcBuffer_HasRemaining(buffer), "Expected the buffer to be emtpy"); + * + * parcBuffer_Release(&buffer); + * + * parcFileOutputStream_Release(&stream); + * } + * @endcode + */ +bool parcFileOutputStream_Write(PARCFileOutputStream *outputStream, PARCBuffer *buffer); +#endif // libparc_parc_FileOutputStream_h diff --git a/libparc/parc/algol/parc_Hash.c b/libparc/parc/algol/parc_Hash.c new file mode 100755 index 00000000..1884c74c --- /dev/null +++ b/libparc/parc/algol/parc_Hash.c @@ -0,0 +1,144 @@ +/* + * 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. + */ + +/** + * This hash is based on FNV-1a, using different lengths. Please see the FNV-1a + * website for details on the algorithm: http://www.isthe.com/chongo/tech/comp/fnv + * + */ +#include <config.h> + +#include <stdint.h> +#include <unistd.h> + +#include <parc/algol/parc_Hash.h> +#include <parc/algol/parc_Object.h> + +struct parc_hash_32bits { + uint32_t accumulator; +}; + +parcObject_ExtendPARCObject(PARCHash32Bits, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + +PARCHash32Bits * +parcHash32Bits_Create(void) +{ + PARCHash32Bits *result = parcObject_CreateInstance(PARCHash32Bits); + if (result != NULL) { + result->accumulator = 0; + } + + return result; +} + +PARCHash32Bits * +parcHash32Bits_Update(PARCHash32Bits *hash, const void *data, size_t length) +{ + hash->accumulator = parcHash32_Data_Cumulative(data, length, hash->accumulator); + return hash; +} + +PARCHash32Bits * +parcHash32Bits_UpdateUint32(PARCHash32Bits *hash, uint32_t value) +{ + hash->accumulator = parcHash32_Data_Cumulative(&value, sizeof(value), hash->accumulator); + return hash; +} + +uint32_t +parcHash32Bits_Hash(PARCHash32Bits *hash) +{ + return hash->accumulator; +} + +parcObject_ImplementAcquire(parcHash32Bits, PARCHash32Bits); + +parcObject_ImplementRelease(parcHash32Bits, PARCHash32Bits); + +/* + * Based on 64-bit FNV-1a + */ +uint64_t +parcHash64_Data(const void *data, size_t len) +{ + // Standard FNV 64-bit offset: see http://www.isthe.com/chongo/tech/comp/fnv/#FNV-param + const uint64_t fnv1a_offset = 0xCBF29CE484222325ULL; + return parcHash64_Data_Cumulative(data, len, fnv1a_offset); +} + +uint64_t +parcHash64_Data_Cumulative(const void *data, size_t len, uint64_t lastValue) +{ + // Standard FNV 64-bit prime: see http://www.isthe.com/chongo/tech/comp/fnv/#FNV-param + const uint64_t fnv1a_prime = 0x00000100000001B3ULL; + uint64_t hash = lastValue; + const char *chardata = data; + + for (size_t i = 0; i < len; i++) { + hash = hash ^ chardata[i]; + hash = hash * fnv1a_prime; + } + + return hash; +} + +uint64_t +parcHash64_Int64(uint64_t int64) +{ + return parcHash64_Data(&int64, sizeof(uint64_t)); +} + +uint64_t +parcHash64_Int32(uint32_t int32) +{ + return parcHash64_Data(&int32, sizeof(uint32_t)); +} + +uint32_t +parcHash32_Data(const void *data, size_t len) +{ + // Standard FNV 32-bit offset: see http://www.isthe.com/chongo/tech/comp/fnv/#FNV-param + const uint32_t fnv1a_offset = 0x811C9DC5; + return parcHash32_Data_Cumulative(data, len, fnv1a_offset); +} + +uint32_t +parcHash32_Data_Cumulative(const void *data, size_t len, uint32_t lastValue) +{ + // Standard FNV 32-bit prime: see http://www.isthe.com/chongo/tech/comp/fnv/#FNV-param + const uint32_t fnv1a_prime = 0x01000193; + uint32_t hash = lastValue; + + const char *chardata = data; + + for (size_t i = 0; i < len; i++) { + hash = hash ^ chardata[i]; + hash = hash * fnv1a_prime; + } + + return hash; +} + +uint32_t +parcHash32_Int64(uint64_t int64) +{ + return parcHash32_Data(&int64, sizeof(uint64_t)); +} + +uint32_t +parcHash32_Int32(uint32_t int32) +{ + return parcHash32_Data(&int32, sizeof(uint32_t)); +} diff --git a/libparc/parc/algol/parc_Hash.h b/libparc/parc/algol/parc_Hash.h new file mode 100755 index 00000000..92227041 --- /dev/null +++ b/libparc/parc/algol/parc_Hash.h @@ -0,0 +1,341 @@ +/* + * 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. + */ + +/** + * @file parc_Hash.h + * @ingroup datastructures + * @brief Implements the FNV-1a 64-bit and 32-bit hashes. + * + * These are some basic hashing functions for blocks of data and integers. They + * generate 64 and 32 bit hashes (They are currently using the FNV-1a algorithm.) + * There is also a cumulative version of the hashes that can be used if intermediary + * hashes are required/useful. + * + */ +#ifndef libparc_parc_Hash_h +#define libparc_parc_Hash_h + +#include <stdint.h> +#include <stdlib.h> + + +struct parc_hash_32bits; +/** + * @typedef PARCHash32Bits + * @brief An accumulator + */ + +typedef struct parc_hash_32bits PARCHash32Bits; + +/** + * Create a 32 bit hash generator + * + * @return A pointer to a `PARCHash32Bits` instance + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +PARCHash32Bits *parcHash32Bits_Create(void); + +/** + * Generate a 32 bit hash from a memory block starting with a previous hash + * + * This is a typed version of {@link parcHash32_Data_Cumulative} + * + * @param [in] hash the value of the last cumulative hash calculated + * @param [in] data pointer to a memory block. + * @param [in] length length of the memory pointed to by data + * + * @return pointer to a {@link PARCHash32Bits} with the cumulative hash + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +PARCHash32Bits *parcHash32Bits_Update(PARCHash32Bits *hash, const void *data, size_t length); + +/** + * Generate a 32 bit hash from a uint32 starting with a previous hash + * Update the cumulative state of the given {@link PARCHash32Bits} + * + * @param [in] hash the value of the last cumulative hash calculated + * @param [in] value The `uint32` to be hashed + * + * @return pointer to a `PARCHash32Bits` with the cumulative hash + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +PARCHash32Bits *parcHash32Bits_UpdateUint32(PARCHash32Bits *hash, uint32_t value); + +/** + * Get the current value of the cummulative state of the given {@link PARCHash32Bits}. + * + * @param [in] hash the value of the last cumulative hash calculated + * + * @return The hash value as an unsigned 32 bit integer + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +uint32_t parcHash32Bits_Hash(PARCHash32Bits *hash); + +/** + * Acquire a new reference to the given {@link PARCHash32Bits} instance. + * + * The reference count to the instance is incremented. + * + * @param [in] hash The instance of `PARCHash32Bits` to which to refer. + * + * @return The same value as the input parameter @p hash + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +PARCHash32Bits *parcHash32Bits_Acquire(const PARCHash32Bits *hash); + +/** + * Release a reference to the given {@link PARCHash32Bits} instance. + * + * Only the last invocation where the reference count is decremented to zero, + * will actually destroy the `PARCHash32Bits`. + * + * @param [in,out] hash is a pointer to the `PARCHash32Bits` reference. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +void parcHash32Bits_Release(PARCHash32Bits **hash); + +/** + * Generate a 64 bit hash from a memory block + * + * This function will generate a 64bit hash from a block of memory. The memory block + * is not modified in any way. + * + * The output of this function can be used as input for the cumulative version of + * this function + * + * @param [in] data A pointer to a memory block. + * @param [in] len The length of the memory pointed to by data + * + * @return hash_64bit A 64 bit hash of the memory block. + * + * Example: + * @code + * + * char * data = "Hello world of hashing"; + * uint64_t myhash = parcHash64_Data(data,strlen(data)); + * + * @endcode + * + * @see {@link parcHash64_Data_Cumulative} + */ +uint64_t parcHash64_Data(const void *data, size_t len); + +/** + * Generate a 64 bit hash from a memory block starting with a previous hash + * + * This function will generate a 64 bit hash from a block of memory and an initial + * hash. This is used to cumulatively calculate a hash of larger block. So, + * one could generate the hash of the first part of the data (A), then calculate the + * hash including the next part of the data (AB) and then the hash of the next + * part (ABC). This is useful for not having to recalculate the hash of the parts + * you have already hashed. + * + * A cumulative hash should have the same value as a full hash of the complete data. + * So cumulative_hash(B,len(B),hash(A)) is equal to hash(AB,len(AB)). + * + * @param [in] data pointer to a memory block. + * @param [in] len length of the memory pointed to by data + * @param [in] lastValue the vale of the last cumulative hash calculated + * + * @return cumulative_hash64 A 64 bit hash of the data _and_ the data that was + * hashed before (represented by lastValue). + * + * Example: + * @code + * + * char * data1 = "1234567890"; + * uint64_t myhash1 = parcHash64_Data(data1,10); + * char * data2 = "abcdefghij"; + * uint64_t myhash2 = parcHash64_Data_Cumulative(data2,10,myhash1); + * char * data3 = "1234567890abcdefghij"; + * uint64_t myhash3 = parcHash64_Data(data3,20); + * // myhash3 will be equal to myhash2 + * + * @endcode + * + * @see {@link parcHash64_Data} + */ +uint64_t parcHash64_Data_Cumulative(const void *data, size_t len, uint64_t lastValue); + +/** + * Generate a 64 bit hash from a 64 bit Integer + * + * This function hashes a 64 bit integer into a 64 bit hash + * + * @param [in] int64 A 64 bit integer + * + * @return hash64 A 64 bit hash of the 64 bit integer + * + * Example: + * @code + * uint64_t id64 = 1234567890123456; + * uint64_t hash64 = parcHash64_Int64(id64); + * @endcode + * + */ +uint64_t parcHash64_Int64(uint64_t int64); + +/** + * Generate a 64 bit hash from a 32 bit Integer + * + * This function hashes a 32 bit integer into a 64 bit hash + * + * @param [in] int32 A 32 bit integer + * + * @return hash64 A 64 bit hash of the 32 bit integer + * + * Example: + * @code + * uint32_t id32 = 70; + * uint64_t hash64 = parcHash64_Int32(id32); + * @endcode + * + */ +uint64_t parcHash64_Int32(uint32_t int32); + +/** + * Generate a 32 bit hash from a memory block + * + * This function will generate a 32bit hash from a block of memory. The memory block + * is not modified in any way. + * The output of this function can be used as input for the cumulative version of + * this function. + * + * @param [in] data pointer to a memory block. + * @param [in] len length of the memory pointed to by data + * + * @return hash_32bit A 32 bit hash of the memory block. + * + * Example: + * @code + * + * char * data = "Hello world of hashing"; + * uint32_t myhash = parcHash32_Data(data,strlen(data)); + * + * @endcode + * + * @see {@link parcHash32_Data_Cumulative} + */ +uint32_t parcHash32_Data(const void *data, size_t len); + +/** + * Generate a 32 bit hash from a memory block starting with a previous hash + * + * This function will generate a 32 bit hash from a block of memory and an initial + * hash. This is used to cumulatively calculate a hash of larger block. So, + * one could generate the hash of the first part of the data (A), then calculate the + * hash including the next part of the data (AB) and then the hash of the next + * part (ABC). This is useful for not having to recalculate the hash of the parts + * you have already hashed. + * + * A cumulative hash should have the same value as a full hash of the complete data. + * So cumulative_hash(B,len(B),hash(A)) is equal to hash(AB,len(AB)). + * + * @param [in] data pointer to a memory block. + * @param [in] len length of the memory pointed to by data + * @param [in] lastValue the vale of the last cumulative hash calculated + * + * @return cumulative_hash32 A 32 bit hash of the data _and_ the data that was + * hashed before (represented by lastValue). + * + * Example: + * @code + * + * char * data1 = "1234567890"; + * uint32_t myhash1 = parcHash32_Data(data1,10); + * char * data2 = "abcdefghij"; + * uint32_t myhash2 = parcHash32_Data_Cumulative(data2,10,myhash1); + * char * data3 = "1234567890abcdefghij"; + * uint32_t myhash3 = parcHash32_Data(data3,20); + * // myhash3 will be equal to myhash2 + * + * @endcode + * + * @see {@link parcHash32_Data} + */ +uint32_t parcHash32_Data_Cumulative(const void *data, size_t len, uint32_t lastValue); + +/** + * Generate a 32 bit hash from a 64 bit Integer + * + * This function hashes a 64 bit integer into a 32 bit hash + * + * @param [in] int64 A 64 bit integer + * + * @return hash32 A 32 bit hash of the 64 bit integer + * + * Example: + * @code + * uint64_t id64 = 1234567890123456; + * uint32_t hash32 = parcHash32_Int64(id64); + * @endcode + * + */ +uint32_t parcHash32_Int64(uint64_t int64); + +/** + * Generate a 32 bit hash from a 32 bit Integer + * + * This function hashes a 32 bit integer into a 32 bit hash + * + * @param [in] int32 A 32 bit integer + * + * @return hash32 A 32 bit hash of the 32 bit integer + * + * Example: + * @code + * uint32_t id32 = 1234567890123456; + * uint32_t hash32 = parcHash32_Int32(id32); + * @endcode + * + */ +uint32_t parcHash32_Int32(uint32_t int32); +#endif // libparc_parc_Hash_h diff --git a/libparc/parc/algol/parc_HashCode.c b/libparc/parc/algol/parc_HashCode.c new file mode 100755 index 00000000..8554429c --- /dev/null +++ b/libparc/parc/algol/parc_HashCode.c @@ -0,0 +1,49 @@ +/* + * 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 <parc/algol/parc_HashCode.h> + +#if PARCHashCodeSize == 64 +static const PARCHashCode _fnv1a_prime = 0x00000100000001B3ULL; +const PARCHashCode parcHashCode_InitialValue = 0xCBF29CE484222325ULL; +#else +static const PARCHashCode _fnv1a_prime = 0x01000193; +const PARCHashCode parcHashCode_InitialValue = 0x811C9DC5; +#endif + +PARCHashCode +parcHashCode_HashImpl(const uint8_t *memory, size_t length, PARCHashCode initialValue) +{ + // Standard FNV 64-bit prime: see http://www.isthe.com/chongo/tech/comp/fnv/#FNV-param + + PARCHashCode hash = initialValue; + + for (size_t i = 0; i < length; i++) { + hash = hash ^ memory[i]; + hash = hash * _fnv1a_prime; + } + + return hash; +} + +PARCHashCode +parcHashCode_HashHashCode(PARCHashCode initialValue, PARCHashCode update) +{ + return parcHashCode_HashImpl((uint8_t *) &update, sizeof(PARCHashCode), initialValue); +} diff --git a/libparc/parc/algol/parc_HashCode.h b/libparc/parc/algol/parc_HashCode.h new file mode 100755 index 00000000..dd572bee --- /dev/null +++ b/libparc/parc/algol/parc_HashCode.h @@ -0,0 +1,94 @@ +/* + * 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. + */ + +/** + * @file parc_HashCode.h + * @ingroup object + * @brief The type returned from implementations of the _HashCode() function. + * + * The size of a PARCHashCode value may be different depending on the compile-time compilation environment. + * + */ +#ifndef PARC_Library_parc_HashCode_h +#define PARC_Library_parc_HashCode_h + +#include <stdint.h> +#include <inttypes.h> +#include <stdlib.h> + +#define PARCHashCodeSize 64 +//#define PARCHashCodeSize 32 + +/** + * @typedef PARCHashCode + * @brief The type returned from implementations of the _HashCode() function. + */ +#if PARCHashCodeSize == 64 +#define PRIPARCHashCode PRIu64 +#define PRIXPARCHashCode PRIX64 +#define PRIxPARCHashCode PRIx64 +typedef uint64_t PARCHashCode; + +#else +#define PRIPARCHashCode PRIu32 +#define PRIXPARCHashCode PRIX32 +#define PRIxPARCHashCode PRIx32 +typedef uint32_t PARCHashCode; + +#endif + +extern const PARCHashCode parcHashCode_InitialValue; + +#define parcHashCode_Hash(_memory_, _length_) parcHashCode_HashImpl(_memory_, _length_, parcHashCode_InitialValue) + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [in] memory A pointer to bytes used to generate the `PARCHashCode`. + * @param [in] length The number of bytes in memory to use to generate the `PARCHashCode` + * @param [in] initialValue An inital value for the `PARCHashCode`. + * + * @return The resulting `PARCHashCode` value. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +PARCHashCode parcHashCode_HashImpl(const uint8_t *memory, size_t length, PARCHashCode initialValue); + +/** + * Hash a PARcHashCode into an existing PARCHashCode. + * + * <#Paragraphs Of Explanation#> + * + * @param [in] initialValue The PARCHashCode initial value + * @param [in] update The PARCHashCode value to update the initial value. + * + * @return The updated PARCHashCode value + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +PARCHashCode parcHashCode_HashHashCode(PARCHashCode initialValue, PARCHashCode update); +#endif diff --git a/libparc/parc/algol/parc_HashCodeTable.c b/libparc/parc/algol/parc_HashCodeTable.c new file mode 100755 index 00000000..ea1945b3 --- /dev/null +++ b/libparc/parc/algol/parc_HashCodeTable.c @@ -0,0 +1,338 @@ +/* + * 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. + */ + +/** + * Implements an open-addressing hash table. We use linear probing of +1 per step. + * + * Table is rehashed when we reach 75% utilization. + * The table is rehashed if we go more than 10 linear probes without being able to insert. + * + * HashCodeTable is a wrapper that holds the key/data management functions. It also + * has LinearAddressingHashTable that is the actual hash table. + * + * This open-addressing table is inefficient for GET or DEL if the element does not exist. + * The whole table needs to be + * + */ +#include <config.h> + +#include <LongBow/runtime.h> + +#include <stdio.h> +#include <string.h> + +#include <parc/algol/parc_HashCodeTable.h> +#include <parc/algol/parc_Memory.h> + +// minimum size if nothing specified +#define MIN_SIZE 256 + +// when we expand, use this factor +#define EXPAND_FACTOR 2 + +#define MAX_PROBE_LENGTH 20 + +typedef enum { + ADD_OK, // we added the key + ADD_DUP, // the key is a duplicate + ADD_NOSPACE // ran out of space +} PARCHashCodeTable_AddResult; + +typedef struct hashtable_entry { + // A hashtable entry is in use if the key is non-null + void *key; + void *data; + HashCodeType hashcode; +} HashTableEntry; + +typedef struct linear_address_hash_table { + HashTableEntry *entries; + + // Number of elements allocated + size_t tableLimit; + + // Number of elements in use + size_t tableSize; + + // When the tableSize equals or exceeds this + // threshold, we should expand and re-hash the table∫ + size_t expandThreshold; +} LinearAddressingHashTable; + +struct parc_hashcode_table { + LinearAddressingHashTable hashtable; + + PARCHashCodeTable_KeyEqualsFunc keyEqualsFunc; + PARCHashCodeTable_HashCodeFunc keyHashCodeFunc; + PARCHashCodeTable_Destroyer keyDestroyer; + PARCHashCodeTable_Destroyer dataDestroyer; + + unsigned expandCount; +}; + +static bool +_findIndex(PARCHashCodeTable *table, const void *key, size_t *outputIndexPtr) +{ + size_t index, start; + HashCodeType hashcode; + LinearAddressingHashTable *innerTable; + + innerTable = &table->hashtable; + hashcode = table->keyHashCodeFunc(key); + index = hashcode % innerTable->tableLimit; + start = index; + + + // check until we've gone MAX_PROBE_LENGTH + unsigned steps = 0; + do { + if (innerTable->entries[index].key != NULL) { + if ((innerTable->entries[index].hashcode == hashcode) && table->keyEqualsFunc(key, innerTable->entries[index].key)) { + // the key already exists in the table + *outputIndexPtr = index; + return true; + } + } + steps++; + index = index + 1; + if (index == innerTable->tableLimit) { + index = 0; + } + } while (index != start && steps < MAX_PROBE_LENGTH); + + return false; +} + +static PARCHashCodeTable_AddResult +_innerTableAdd(LinearAddressingHashTable *innerTable, PARCHashCodeTable_KeyEqualsFunc keyEqualsFunc, + HashCodeType hashcode, void *key, void *data) +{ + size_t index = hashcode % innerTable->tableLimit; + + unsigned steps = 0; + + // we know the size < limit, so it will fit eventually + while (steps < MAX_PROBE_LENGTH) { + if (innerTable->entries[index].key == NULL) { + innerTable->entries[index].hashcode = hashcode; + innerTable->entries[index].key = key; + innerTable->entries[index].data = data; + innerTable->tableSize++; + return ADD_OK; + } + + if ((innerTable->entries[index].hashcode == hashcode) && keyEqualsFunc(key, innerTable->entries[index].key)) { + // the key already exists in the table + return ADD_DUP; + } + + steps++; + index = index + 1; + if (index == innerTable->tableLimit) { + index = 0; + } + } + + return ADD_NOSPACE; +} + +static PARCHashCodeTable_AddResult +_rehash(LinearAddressingHashTable *old_table, LinearAddressingHashTable *new_table, PARCHashCodeTable_KeyEqualsFunc keyEqualsFunc) +{ + size_t i; + for (i = 0; i < old_table->tableLimit; i++) { + if (old_table->entries[i].key != NULL) { + PARCHashCodeTable_AddResult result = _innerTableAdd(new_table, keyEqualsFunc, old_table->entries[i].hashcode, + old_table->entries[i].key, old_table->entries[i].data); + if (result != ADD_OK) { + return result; + } + } + } + return ADD_OK; +} + +static void +_expand(PARCHashCodeTable *hashCodeTable) +{ + LinearAddressingHashTable temp_table; + LinearAddressingHashTable *old_table = &hashCodeTable->hashtable; + + size_t expandby = EXPAND_FACTOR; + + // start with a copy of the current table + PARCHashCodeTable_AddResult result = ADD_OK; + do { + hashCodeTable->expandCount++; + + temp_table.tableSize = 0; + temp_table.tableLimit = old_table->tableLimit * expandby; + temp_table.expandThreshold = temp_table.tableLimit - temp_table.tableLimit / 4; + temp_table.entries = parcMemory_AllocateAndClear(temp_table.tableLimit * sizeof(HashTableEntry)); + assertNotNull(temp_table.entries, "parcMemory_AllocateAndClear(%zu) returned NULL", temp_table.tableLimit * sizeof(HashTableEntry)); + + result = _rehash(old_table, &temp_table, hashCodeTable->keyEqualsFunc); + if (result == ADD_NOSPACE) { + // could not rehash, so expand by more and try again + parcMemory_Deallocate((void **) &(temp_table.entries)); + expandby++; + } + } while (result == ADD_NOSPACE); + + parcMemory_Deallocate((void **) &old_table->entries); + hashCodeTable->hashtable = temp_table; +} + +PARCHashCodeTable * +parcHashCodeTable_Create_Size(PARCHashCodeTable_KeyEqualsFunc keyEqualsFunc, + PARCHashCodeTable_HashCodeFunc keyHashCodeFunc, + PARCHashCodeTable_Destroyer keyDestroyer, + PARCHashCodeTable_Destroyer dataDestroyer, + size_t minimumSize) +{ + PARCHashCodeTable *table = parcMemory_AllocateAndClear(sizeof(PARCHashCodeTable)); + assertNotNull(table, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(PARCHashCodeTable)); + + assertNotNull(keyEqualsFunc, "keyEqualsFunc must be non-null"); + assertNotNull(keyHashCodeFunc, "keyHashCodeFunc must be non-null"); + assertTrue(minimumSize > 0, "minimumSize must be greater than zero"); + + table->keyEqualsFunc = keyEqualsFunc; + table->keyHashCodeFunc = keyHashCodeFunc; + table->keyDestroyer = keyDestroyer; + table->dataDestroyer = dataDestroyer; + + table->hashtable.entries = parcMemory_AllocateAndClear(minimumSize * sizeof(HashTableEntry)); + assertNotNull(table->hashtable.entries, "parcMemory_AllocateAndClear(%zu) returned NULL", minimumSize * sizeof(HashTableEntry)); + table->hashtable.tableLimit = minimumSize; + table->hashtable.tableSize = 0; + + memset(table->hashtable.entries, 0, minimumSize * sizeof(HashTableEntry)); + + // expand at 75% utilization + table->hashtable.expandThreshold = minimumSize - minimumSize / 4; + + return table; +} + +PARCHashCodeTable * +parcHashCodeTable_Create(PARCHashCodeTable_KeyEqualsFunc keyEqualsFunc, + PARCHashCodeTable_HashCodeFunc keyHashCodeFunc, + PARCHashCodeTable_Destroyer keyDestroyer, + PARCHashCodeTable_Destroyer dataDestroyer) +{ + return parcHashCodeTable_Create_Size(keyEqualsFunc, keyHashCodeFunc, keyDestroyer, dataDestroyer, MIN_SIZE); +} + +void +parcHashCodeTable_Destroy(PARCHashCodeTable **tablePtr) +{ + assertNotNull(tablePtr, "Parameter must be non-null double pointer"); + assertNotNull(*tablePtr, "Parameter must dereference to non-null pointer"); + PARCHashCodeTable *table = *tablePtr; + size_t i; + + for (i = 0; i < table->hashtable.tableLimit; i++) { + if (table->hashtable.entries[i].key != NULL) { + if (table->keyDestroyer) { + table->keyDestroyer(&table->hashtable.entries[i].key); + } + + if (table->dataDestroyer) { + table->dataDestroyer(&table->hashtable.entries[i].data); + } + } + } + + parcMemory_Deallocate((void **) &(table->hashtable.entries)); + parcMemory_Deallocate((void **) &table); + *tablePtr = NULL; +} + +bool +parcHashCodeTable_Add(PARCHashCodeTable *table, void *key, void *data) +{ + assertNotNull(table, "Parameter table must be non-null"); + assertNotNull(key, "Parameter key must be non-null"); + assertNotNull(data, "Parameter data must be non-null"); + + if (table->hashtable.tableSize >= table->hashtable.expandThreshold) { + _expand(table); + } + + HashCodeType hashcode = table->keyHashCodeFunc(key); + + PARCHashCodeTable_AddResult result = ADD_OK; + do { + result = _innerTableAdd(&table->hashtable, table->keyEqualsFunc, hashcode, key, data); + if (result == ADD_NOSPACE) { + _expand(table); + } + } while (result == ADD_NOSPACE); + + return (result == ADD_OK); +} + +void +parcHashCodeTable_Del(PARCHashCodeTable *table, const void *key) +{ + size_t index; + bool found; + + assertNotNull(table, "Parameter table must be non-null"); + assertNotNull(key, "parameter key must be non-null"); + + found = _findIndex(table, key, &index); + + if (found) { + assertTrue(table->hashtable.tableSize > 0, "Illegal state: found entry in a hash table with 0 size"); + + if (table->keyDestroyer) { + table->keyDestroyer(&table->hashtable.entries[index].key); + } + + if (table->dataDestroyer) { + table->dataDestroyer(&table->hashtable.entries[index].data); + } + + memset(&table->hashtable.entries[index], 0, sizeof(HashTableEntry)); + + table->hashtable.tableSize--; + } +} + +void * +parcHashCodeTable_Get(PARCHashCodeTable *table, const void *key) +{ + size_t index; + + assertNotNull(table, "Parameter table must be non-null"); + assertNotNull(key, "parameter key must be non-null"); + + bool found = _findIndex(table, key, &index); + + if (found) { + return table->hashtable.entries[index].data; + } + + return NULL; +} + +size_t +parcHashCodeTable_Length(const PARCHashCodeTable *table) +{ + assertNotNull(table, "Parameter table must be non-null"); + return table->hashtable.tableSize; +} diff --git a/libparc/parc/algol/parc_HashCodeTable.h b/libparc/parc/algol/parc_HashCodeTable.h new file mode 100644 index 00000000..58b3d016 --- /dev/null +++ b/libparc/parc/algol/parc_HashCodeTable.h @@ -0,0 +1,168 @@ +/* + * 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. + */ + +/** + * @file parc_HashCodeTable.h + * @ingroup datastructures + * + * A hashcode table requires the user to specify their own hash function + * to operate on the object type being inserted. + * + */ +#ifndef libparc_parc_HashCodeTable_h +#define libparc_parc_HashCodeTable_h + +#include <stdint.h> +#include <stdlib.h> +#include <stdbool.h> + +#include <parc/algol/parc_HashCode.h> + +struct parc_hashcode_table; +typedef struct parc_hashcode_table PARCHashCodeTable; + +typedef PARCHashCode HashCodeType; + + +/** + * @typedef PARCHashCodeTable_KeyEqualsFunc + * @brief Are two keys equal? + * + */ + +typedef bool (*PARCHashCodeTable_KeyEqualsFunc)(const void *keyA, const void *keyB); + +/** + * @typedef PARCHashCodeTable_HashCodeFunc + */ + +typedef HashCodeType (*PARCHashCodeTable_HashCodeFunc)(const void *keyA); + +/** + * @typedef PARCHashCodeTable_Destroyer + */ + +typedef void (*PARCHashCodeTable_Destroyer)(void **keyOrDataPtr); + +/** + * Create a Hash Table based on hash codes. + * + * @param [in] keyEqualsFunc Tests keys for equality. + * @param [in] keyHashCodeFunc Returns the hash code of a key + * @param [in] keyDestroyer Called on Remove or Destroy to free stored keys, may be NULL. + * @param [in] dataDestroyer Called on Remove or Destroy to free stored data, may be NULL. + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCHashCodeTable *parcHashCodeTable_Create(PARCHashCodeTable_KeyEqualsFunc keyEqualsFunc, + PARCHashCodeTable_HashCodeFunc keyHashCodeFunc, + PARCHashCodeTable_Destroyer keyDestroyer, + PARCHashCodeTable_Destroyer dataDestroyer); + + +/** + * Create a Hash Table based on hash codes. + * + * @param [in] keyEqualsFunc Tests keys for equality. + * @param [in] keyHashCodeFunc Returns the hash code of a key + * @param [in] keyDestroyer Called on Remove or Destroy to free stored keys, may be NULL. + * @param [in] dataDestroyer Called on Remove or Destroy to free stored data, may be NULL. + * @param [in] minimumSize The minimum size of the table + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCHashCodeTable *parcHashCodeTable_Create_Size(PARCHashCodeTable_KeyEqualsFunc keyEqualsFunc, + PARCHashCodeTable_HashCodeFunc keyHashCodeFunc, + PARCHashCodeTable_Destroyer keyDestroyer, + PARCHashCodeTable_Destroyer dataDestroyer, + size_t minimumSize); + +/** + * Destroy the table and free all saved objects + * + * @param [in,out] tablePtr is a pointer to the `PARCHashCodeTable` reference. + * + * + * Example: + * @code + * <#example#> + * @endcode + */ +void parcHashCodeTable_Destroy(PARCHashCodeTable **tablePtr); + +/** + * Add an element to the hash table. + * @param [in,out] table The key, must be usable with the {@link PARCHashCodeTable_KeyEqualsFunc} and {@link PARCHashCodeTable_HashCodeFunc}. + * @param [in] key The key, must be usable with the `keyEqualsFunc` and `keyHashCodeFunc`. + * @param [in] data The value, must not be NULL + * + * @return true if key did not exist and data was added. Returns false if key exists or error. + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool parcHashCodeTable_Add(PARCHashCodeTable *table, void *key, void *data); + +/** + * Removes a key from an instance of `PARCHashCodeTable`, freeing key and data memory. Does nothing if key does not + * exist in the table. + * + * @param [in,out] table The instance of `PARCHashCodeTable` from which the key will be removed. + * @param [in] key The key, must be usable with the {@link PARCHashCodeTable_KeyEqualsFunc} and {@link PARCHashCodeTable_HashCodeFunc}. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void parcHashCodeTable_Del(PARCHashCodeTable *table, const void *key); + +/** + * Returns the key value, or NULL if the key does not exist + * + * @param [in] table The instance of `PARCHashCodeTable` from which the the value will be retrieved. + * @param [in] key The key to identify the desired value. + * + * @return A pointer to the value of the specified key. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void *parcHashCodeTable_Get(PARCHashCodeTable *table, const void *key); + +/** + * Returns the number of entries in the table + * + * + * @param [in] table The specified `PARCHashCodeTable` instance. + * @return The number of entries in @p table. + * + * Example: + * @code + * <#example#> + * @endcode + */ +size_t parcHashCodeTable_Length(const PARCHashCodeTable *table); +#endif // libparc_parc_HashCodeTable_h diff --git a/libparc/parc/algol/parc_HashMap.c b/libparc/parc/algol/parc_HashMap.c new file mode 100644 index 00000000..35a4414f --- /dev/null +++ b/libparc/parc/algol/parc_HashMap.c @@ -0,0 +1,666 @@ +/* + * 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 <LongBow/runtime.h> + +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_DisplayIndented.h> +#include <parc/algol/parc_Memory.h> + +#include "parc_HashMap.h" +#include "parc_LinkedList.h" + +#include <math.h> + +static const uint32_t DEFAULT_CAPACITY = 43; + +typedef struct PARCHashMapEntry { + PARCObject *key; + PARCObject *value; +} _PARCHashMapEntry; + + +static bool +_parcHashMapEntry_IsValid(_PARCHashMapEntry *hashEntry) +{ + bool result = false; + + if (hashEntry) { + if (parcObject_IsValid(hashEntry->key)) { + if (parcObject_IsValid(hashEntry->value)) { + result = true; + } + } + } + + return result; +} + +static void +_parcHashMapEntry_Finalize(_PARCHashMapEntry **instancePtr) +{ + assertNotNull(instancePtr, "Parameter must be a non-null pointer to a PARCHashMap pointer."); + _PARCHashMapEntry *hashMapEntry = *instancePtr; + + _parcHashMapEntry_IsValid(hashMapEntry); + + parcObject_Release(&hashMapEntry->key); + parcObject_Release(&hashMapEntry->value); +} + +static bool +_parcHashMapEntry_Equals(const _PARCHashMapEntry *a, const _PARCHashMapEntry *b) +{ + return (parcObject_Equals(a->key, b->key) && parcObject_Equals(a->value, b->value)); +} + + +static PARCHashCode +_parcHashMapEntry_HashCode(const _PARCHashMapEntry *entry) +{ + return parcObject_HashCode(entry->key); +} + + +struct PARCHashMap { + PARCLinkedList **buckets; + size_t minCapacity; + size_t capacity; + size_t size; + double maxLoadFactor; + double minLoadFactor; +}; + +static _PARCHashMapEntry * +_parcHashMap_GetEntry(const PARCHashMap *hashMap, const PARCObject *key) +{ + PARCHashCode keyHash = parcObject_HashCode(key); + + int bucket = keyHash % hashMap->capacity; + + _PARCHashMapEntry *result = NULL; + + if (hashMap->buckets[bucket] != NULL) { + PARCIterator *iterator = parcLinkedList_CreateIterator(hashMap->buckets[bucket]); + + while (parcIterator_HasNext(iterator)) { + _PARCHashMapEntry *entry = parcIterator_Next(iterator); + if (parcObject_Equals(key, entry->key)) { + result = entry; + break; + } + } + parcIterator_Release(&iterator); + } + + return result; +} + +//static parcObject_ImplementAcquire(parcHashMapEntry, _PARCHashMapEntry); + +static parcObject_ImplementRelease(_parcHashMapEntry, _PARCHashMapEntry); + +parcObject_ExtendPARCObject(_PARCHashMapEntry, _parcHashMapEntry_Finalize, NULL, NULL, _parcHashMapEntry_Equals, NULL, _parcHashMapEntry_HashCode, NULL); + +static _PARCHashMapEntry * +_parcHashMapEntry_Create(const PARCObject *key, const PARCObject *value) +{ + parcObject_OptionalAssertValid(key); + parcObject_OptionalAssertValid(value); + + _PARCHashMapEntry *result = parcObject_CreateInstance(_PARCHashMapEntry); + + result->key = parcObject_Copy(key); + result->value = parcObject_Acquire(value); + + return result; +} + +static void +_parcHashMap_Finalize(PARCHashMap **instancePtr) +{ + assertNotNull(instancePtr, "Parameter must be a non-null pointer to a PARCHashMap pointer."); + PARCHashMap *hashMap = *instancePtr; + + for (unsigned int i = 0; i < hashMap->capacity; i++) { + if (hashMap->buckets[i] != NULL) { + parcLinkedList_Release(&hashMap->buckets[i]); + } + } + + parcMemory_Deallocate(&hashMap->buckets); + + /* cleanup the instance fields here */ +} + +parcObject_ImplementAcquire(parcHashMap, PARCHashMap); + +parcObject_ImplementRelease(parcHashMap, PARCHashMap); + +parcObject_ExtendPARCObject(PARCHashMap, _parcHashMap_Finalize, parcHashMap_Copy, parcHashMap_ToString, parcHashMap_Equals, NULL, parcHashMap_HashCode, parcHashMap_ToJSON); + +void +parcHashMap_AssertValid(const PARCHashMap *instance) +{ + assertTrue(parcHashMap_IsValid(instance), + "PARCHashMap is not valid."); +} + +PARCHashMap * +parcHashMap_CreateCapacity(unsigned int capacity) +{ + PARCHashMap *result = parcObject_CreateInstance(PARCHashMap); + + if (result != NULL) { + if (capacity == 0) { + capacity = DEFAULT_CAPACITY; + } + + result->minCapacity = capacity; + result->capacity = capacity; + result->size = 0; + result->maxLoadFactor = 0.75; + result->minLoadFactor = result->maxLoadFactor / 3.0; + result->buckets = parcMemory_AllocateAndClear(capacity * sizeof(PARCLinkedList*)); + } + + return result; +} + +PARCHashMap * +parcHashMap_Create(void) +{ + PARCHashMap *result = parcHashMap_CreateCapacity(DEFAULT_CAPACITY); + + return result; +} + +PARCHashMap * +parcHashMap_Copy(const PARCHashMap *original) +{ + parcHashMap_OptionalAssertValid(original); + + PARCHashMap *result = parcObject_CreateInstance(PARCHashMap); + + result->capacity = original->capacity; + result->minCapacity = original->minCapacity; + result->maxLoadFactor = original->maxLoadFactor; + result->minLoadFactor = original->minLoadFactor; + result->size = original->size; + result->buckets = parcMemory_Allocate(result->capacity * sizeof(PARCLinkedList*)); + + for (unsigned int i = 0; i < result->capacity; i++) { + result->buckets[i] = NULL; + if (original->buckets[i] != NULL) { + result->buckets[i] = parcLinkedList_Copy(original->buckets[i]); + } + } + + return result; +} + +void +parcHashMap_Display(const PARCHashMap *hashMap, int indentation) +{ + parcDisplayIndented_PrintLine(indentation, "PARCHashMap@%p {", hashMap); + + PARCIterator *iterator = parcHashMap_CreateKeyIterator((PARCHashMap *) hashMap); + + while (parcIterator_HasNext(iterator)) { + PARCObject *keyObject = parcIterator_Next(iterator); + const PARCObject *valueObject = parcHashMap_Get(hashMap, keyObject); + char *key = parcObject_ToString(keyObject); + char *value = parcObject_ToString(valueObject); + parcDisplayIndented_PrintLine(indentation + 1, "%s -> %s", key, value); + parcMemory_Deallocate(&key); + parcMemory_Deallocate(&value); + } + parcIterator_Release(&iterator); + + parcDisplayIndented_PrintLine(indentation, "}"); +} + +bool +parcHashMap_Equals(const PARCHashMap *x, const PARCHashMap *y) +{ + bool result = false; + + if (x == y) { + result = true; + } else if (x == NULL || y == NULL) { + result = false; + } else { + parcHashMap_OptionalAssertValid(x); + parcHashMap_OptionalAssertValid(y); + + if (x->capacity == y->capacity) { + if (x->size == y->size) { + result = true; + for (unsigned int i = 0; (i < x->capacity) && result; i++) { + if ((x->buckets[i] == NULL) || (y->buckets[i] == NULL)) { + result = (x->buckets[i] == y->buckets[i]); + } else { + // For each item in an X bucket, it must be in the Y bucket. + result = parcLinkedList_SetEquals(x->buckets[i], y->buckets[i]); + } + } + } + } + } + + return result; +} + +PARCHashCode +parcHashMap_HashCode(const PARCHashMap *hashMap) +{ + parcHashMap_OptionalAssertValid(hashMap); + + PARCHashCode result = 0; + + for (unsigned int i = 0; i < hashMap->capacity; i++) { + if (hashMap->buckets[i] != NULL) { + result += parcLinkedList_HashCode(hashMap->buckets[i]); + } + } + + return result; +} + +bool +parcHashMap_IsValid(const PARCHashMap *map) +{ + bool result = false; + + if (map != NULL) { + if (parcObject_IsValid(map)) { + result = true; + + for (unsigned int i = 0; i < map->capacity; i++) { + if (map->buckets[i] != NULL) { + if (parcLinkedList_IsValid(map->buckets[i]) == false) { + result = false; + break; + } + } + } + } + } + + return result; +} + +PARCJSON * +parcHashMap_ToJSON(const PARCHashMap *hashMap) +{ + parcHashMap_OptionalAssertValid(hashMap); + + PARCJSON *result = parcJSON_Create(); + + PARCIterator *iterator = parcHashMap_CreateKeyIterator((PARCHashMap *) hashMap); + + while (parcIterator_HasNext(iterator)) { + PARCObject *keyObject = parcIterator_Next(iterator); + const PARCObject *valueObject = parcHashMap_Get(hashMap, keyObject); + char *key = parcObject_ToString(keyObject); + PARCJSON *value = parcObject_ToJSON(valueObject); + + parcJSON_AddObject(result, key, value); + + parcMemory_Deallocate(&key); + parcJSON_Release(&value); + } + + parcIterator_Release(&iterator); + + + return result; +} + +PARCBufferComposer * +parcHashMap_BuildString(const PARCHashMap *hashMap, PARCBufferComposer *composer) +{ + PARCIterator *iterator = parcHashMap_CreateKeyIterator((PARCHashMap *) hashMap); + + while (parcIterator_HasNext(iterator)) { + PARCObject *keyObject = parcIterator_Next(iterator); + const PARCObject *valueObject = parcHashMap_Get(hashMap, keyObject); + char *key = parcObject_ToString(keyObject); + char *value = parcObject_ToString(valueObject); + parcBufferComposer_Format(composer, "%s -> %s\n", key, value); + parcMemory_Deallocate(&key); + parcMemory_Deallocate(&value); + } + + parcIterator_Release(&iterator); + + return composer; +} + +char * +parcHashMap_ToString(const PARCHashMap *hashMap) +{ + parcHashMap_OptionalAssertValid(hashMap); + char *result = NULL; + + PARCBufferComposer *composer = parcBufferComposer_Create(); + if (composer != NULL) { + parcHashMap_BuildString(hashMap, composer); + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + parcBufferComposer_Release(&composer); + } + + return result; +} + +bool +parcHashMap_Contains(PARCHashMap *hashMap, const PARCObject *key) +{ + PARCObject *result = NULL; + + _PARCHashMapEntry *entry = _parcHashMap_GetEntry(hashMap, key); + if (entry != NULL) { + result = entry->value; + } + + return result; +} + +static void +_parcHashMap_Resize(PARCHashMap *hashMap, size_t newCapacity) +{ + if (newCapacity < hashMap->minCapacity) { + return; + } + + PARCLinkedList **newBuckets = parcMemory_AllocateAndClear(newCapacity * sizeof(PARCLinkedList*)); + + for (unsigned int i = 0; i < hashMap->capacity; i++) { + if (hashMap->buckets[i] != NULL) { + if (!parcLinkedList_IsEmpty(hashMap->buckets[i])) { + PARCIterator *elementIt = parcLinkedList_CreateIterator(hashMap->buckets[i]); + while (parcIterator_HasNext(elementIt)) { + _PARCHashMapEntry *entry = parcIterator_Next(elementIt); + PARCHashCode keyHash = parcObject_HashCode(entry->key); + int newBucket = keyHash % newCapacity; + if (newBuckets[newBucket] == NULL) { + newBuckets[newBucket] = parcLinkedList_Create(); + } + parcLinkedList_Append(newBuckets[newBucket], entry); + } + parcIterator_Release(&elementIt); + } + parcLinkedList_Release(&hashMap->buckets[i]); + } + } + PARCLinkedList **cleanupBuckets = hashMap->buckets; + hashMap->buckets = newBuckets; + hashMap->capacity = newCapacity; + + parcMemory_Deallocate(&cleanupBuckets); +} + +bool +parcHashMap_Remove(PARCHashMap *hashMap, const PARCObject *key) +{ + PARCHashCode keyHash = parcObject_HashCode(key); + + int bucket = keyHash % hashMap->capacity; + + bool result = false; + + if (hashMap->buckets[bucket] != NULL) { + PARCIterator *iterator = parcLinkedList_CreateIterator(hashMap->buckets[bucket]); + + while (parcIterator_HasNext(iterator)) { + _PARCHashMapEntry *entry = parcIterator_Next(iterator); + if (parcObject_Equals(key, entry->key)) { + parcIterator_Remove(iterator); + hashMap->size--; + result = true; + break; + } + } + parcIterator_Release(&iterator); + } + + // When expanded by 2 the load factor goes from .75 (3/4) to .375 (3/8), if + // we compress by 2 when the load factor is .25 (1/4) the load + // factor becomes .5 (1/2). + double loadFactor = (double) hashMap->size / (double) hashMap->capacity; + if (loadFactor <= (hashMap->minLoadFactor)) { + _parcHashMap_Resize(hashMap, hashMap->capacity / 2); + } + + return result; +} + +#include <stdio.h> + +PARCHashMap * +parcHashMap_Put(PARCHashMap *hashMap, const PARCObject *key, const PARCObject *value) +{ + // When expanded by 2 the load factor goes from .75 (3/4) to .375 (3/8), if + // we compress by 2 when the load factor is .25 (1/4) the load + // factor becomes .5 (1/2). + double loadFactor = (double) hashMap->size / (double) hashMap->capacity; + if (loadFactor >= hashMap->maxLoadFactor) { + _parcHashMap_Resize(hashMap, hashMap->capacity * 2); + } + + _PARCHashMapEntry *entry = _parcHashMap_GetEntry(hashMap, key); + + if (entry != NULL) { + if (entry->value != value) { + parcObject_Release(&entry->value); + entry->value = parcObject_Acquire(value); + } + } else { + entry = _parcHashMapEntry_Create(key, value); + + PARCHashCode keyHash = parcObject_HashCode(key); + int bucket = keyHash % hashMap->capacity; + + if (hashMap->buckets[bucket] == NULL) { + hashMap->buckets[bucket] = parcLinkedList_Create(); + } + parcLinkedList_Append(hashMap->buckets[bucket], entry); + hashMap->size++; + _parcHashMapEntry_Release(&entry); + } + + return hashMap; +} + +const PARCObject * +parcHashMap_Get(const PARCHashMap *hashMap, const PARCObject *key) +{ + PARCObject *result = NULL; + + _PARCHashMapEntry *entry = _parcHashMap_GetEntry(hashMap, key); + if (entry != NULL) { + result = entry->value; + } + + return result; +} + +size_t +parcHashMap_Size(const PARCHashMap *hashMap) +{ + parcHashMap_OptionalAssertValid(hashMap); + return hashMap->size; +} + +double +parcHashMap_GetClusteringNumber(const PARCHashMap *hashMap) +{ + // This function compute the standard deviation of the chain-lengths + // from a value of 1.0 (as opposed to the mean) and weights the + // result by in inverse of the current load factor. The deviation + // from 1.0 is used because the hashmap's max load factor is < 1.0 and + // thus the ideal average chain-length is 1.0 + // + // A result of 0.0 equates to an ideal distribution, a result of ~1.0 should + // represent a fairly normal or random distribution, and a result > 1.5 or so + // implies some amount of undesirable clumping may be happening. + + size_t totalLength = 0; + double variance = 0; + + // Compute the variance vs 1.0 + for (size_t i = 0; i < hashMap->capacity; ++i) { + if (hashMap->buckets[i] != NULL) { + size_t bucketSize = parcLinkedList_Size(hashMap->buckets[i]); + totalLength += bucketSize; + variance += (bucketSize - 1) * (bucketSize - 1); //Variance relative to 1 + } + } + variance /= ((double) totalLength); + + // Compute the standard deviation + double standardDeviation = sqrt(variance); + + // Weight the standard deviation by the inverse of the current load factor + return standardDeviation * ((double) hashMap->capacity / (double) totalLength); +} + +typedef struct { + PARCHashMap *map; + int bucket; + PARCIterator *listIterator; + _PARCHashMapEntry *current; +} _PARCHashMapIterator; + +static _PARCHashMapIterator * +_parcHashMap_Init(PARCHashMap *map __attribute__((unused))) +{ + _PARCHashMapIterator *state = parcMemory_AllocateAndClear(sizeof(_PARCHashMapIterator)); + + if (state != NULL) { + state->map = map; + state->bucket = 0; + state->listIterator = NULL; + for (size_t i = 0; i < map->capacity; ++i) { + if (map->buckets[i] != NULL) { + state->bucket = i; + state->listIterator = parcLinkedList_CreateIterator(map->buckets[i]); + break; + } + } + + trapOutOfMemoryIf(state->listIterator == NULL, "Cannot create parcLinkedList_CreateIterator"); + } + + return state; +} + +static bool +_parcHashMap_Fini(PARCHashMap *map __attribute__((unused)), _PARCHashMapIterator *state __attribute__((unused))) +{ + if (state->listIterator != NULL) { + parcIterator_Release(&state->listIterator); + } + parcMemory_Deallocate(&state); + return true; +} + +static _PARCHashMapIterator * +_parcHashMap_Next(PARCHashMap *map __attribute__((unused)), _PARCHashMapIterator *state) +{ + _PARCHashMapEntry *result = parcIterator_Next(state->listIterator); + state->current = result; + return state; +} + +static void +_parcHashMap_Remove(PARCHashMap *map, _PARCHashMapIterator **statePtr __attribute__((unused))) +{ + _PARCHashMapIterator *state = *statePtr; + + if (state->listIterator != NULL) { + parcIterator_Remove(state->listIterator); + map->size--; + } +} + +static bool +_parcHashMap_HasNext(PARCHashMap *map __attribute__((unused)), _PARCHashMapIterator *state) +{ + bool result = false; + if (state->listIterator != NULL) { + if (parcIterator_HasNext(state->listIterator)) { + result = true; + } else { + while ((result == false) && (++state->bucket < map->capacity)) { + if (map->buckets[state->bucket] != NULL) { + parcIterator_Release(&state->listIterator); + state->listIterator = parcLinkedList_CreateIterator(map->buckets[state->bucket]); + trapOutOfMemoryIf(state->listIterator == NULL, "Cannot create parcLinkedList_CreateIterator"); + result = parcIterator_HasNext(state->listIterator); + } + } + } + } + return result; +} + +static PARCObject * +_parcHashMapValue_Element(PARCHashMap *map __attribute__((unused)), const _PARCHashMapIterator *state) +{ + return state->current->value; +} + +static PARCObject * +_parcHashMapKey_Element(PARCHashMap *map __attribute__((unused)), const _PARCHashMapIterator *state) +{ + return state->current->key; +} + +PARCIterator * +parcHashMap_CreateValueIterator(PARCHashMap *hashMap) +{ + PARCIterator *iterator = parcIterator_Create(hashMap, + (void *(*)(PARCObject *))_parcHashMap_Init, + (bool (*)(PARCObject *, void *))_parcHashMap_HasNext, + (void *(*)(PARCObject *, void *))_parcHashMap_Next, + (void (*)(PARCObject *, void **))_parcHashMap_Remove, + (void *(*)(PARCObject *, void *))_parcHashMapValue_Element, + (void (*)(PARCObject *, void *))_parcHashMap_Fini, + NULL); + + return iterator; +} + + +PARCIterator * +parcHashMap_CreateKeyIterator(PARCHashMap *hashMap) +{ + PARCIterator *iterator = parcIterator_Create(hashMap, + (void *(*)(PARCObject *))_parcHashMap_Init, + (bool (*)(PARCObject *, void *))_parcHashMap_HasNext, + (void *(*)(PARCObject *, void *))_parcHashMap_Next, + (void (*)(PARCObject *, void **))_parcHashMap_Remove, + (void *(*)(PARCObject *, void *))_parcHashMapKey_Element, + (void (*)(PARCObject *, void *))_parcHashMap_Fini, + NULL); + + return iterator; +} diff --git a/libparc/parc/algol/parc_HashMap.h b/libparc/parc/algol/parc_HashMap.h new file mode 100755 index 00000000..3ab26cd9 --- /dev/null +++ b/libparc/parc/algol/parc_HashMap.h @@ -0,0 +1,622 @@ +/* + * 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. + */ + +/** + * @file parc_HashMap.h + * @ingroup datastructures + * @brief <#Brief Description#> + * + * <#Detailed Description#> + * + */ +#ifndef PARCLibrary_parc_HashMap +#define PARCLibrary_parc_HashMap +#include <stdbool.h> + +#include <parc/algol/parc_JSON.h> +#include <parc/algol/parc_HashCode.h> +#include <parc/algol/parc_Iterator.h> + +struct PARCHashMap; +typedef struct PARCHashMap PARCHashMap; + +/** + * Increase the number of references to a `PARCHashMap` instance. + * + * Note that new `PARCHashMap` is not created, + * only that the given `PARCHashMap` reference count is incremented. + * Discard the reference by invoking `parcHashMap_Release`. + * + * @param [in] instance A pointer to a valid PARCHashMap instance. + * + * @return The same value as @p instance. + * + * Example: + * @code + * { + * PARCHashMap *a = parcHashMap_Create(); + * + * PARCHashMap *b = parcHashMap_Acquire(); + * + * parcHashMap_Release(&a); + * parcHashMap_Release(&b); + * } + * @endcode + */ +PARCHashMap *parcHashMap_Acquire(const PARCHashMap *instance); + +#ifdef PARCLibrary_DISABLE_VALIDATION +# define parcHashMap_OptionalAssertValid(_instance_) +#else +# define parcHashMap_OptionalAssertValid(_instance_) parcHashMap_AssertValid(_instance_) +#endif + +/** + * Assert that the given `PARCHashMap` instance is valid. + * + * @param [in] instance A pointer to a valid PARCHashMap instance. + * + * Example: + * @code + * { + * PARCHashMap *a = parcHashMap_Create(); + * + * parcHashMap_AssertValid(a); + * + * printf("Instance is valid.\n"); + * + * parcHashMap_Release(&b); + * } + * @endcode + */ +void parcHashMap_AssertValid(const PARCHashMap *instance); + +/** + * Constructs an empty `PARCHashMap` with a default minimum number of 'buckets'. + * + * The capacity will expand and contract as needed to keep load factor table + * below the max load factor of 0.75 and above the minimum load factor or 0.25. + * The default minimum number of buckets is 42. + * + * @return non-NULL A pointer to a valid PARCHashMap instance. + * @return NULL An error occurred. + * + * Example: + * @code + * { + * PARCHashMap *a = parcHashMap_Create(); + * + * parcHashMap_Release(&a); + * } + * @endcode + */ +PARCHashMap *parcHashMap_Create(void); + +/** + * Constructs an empty `PARCHashMap` with the specified minimum number of 'buckets'. + * + * The capacity will expand and contract as needed to keep load factor table + * below the max load factor of 0.75 and above the minimum load factor or 0.25. + * + * @param [in] capacity The minimum number of buckets. Must be greater than 0. + * + * @return non-NULL A pointer to a valid PARCHashMap instance. + * @return NULL An error occurred. + * + * Example: + * @code + * { + * PARCHashMap *a = parcHashMap_CreateCapacity(43); + * + * parcHashMap_Release(&a); + * } + * @endcode + */ +PARCHashMap *parcHashMap_CreateCapacity(unsigned int capacity); + +/** + * Create an independent copy the given `PARCBuffer` + * + * A new buffer is created as a complete copy of the original. + * + * @param [in] original A pointer to a valid PARCHashMap instance. + * + * @return NULL Memory could not be allocated. + * @return non-NULL A pointer to a new `PARCHashMap` instance. + * + * Example: + * @code + * { + * PARCHashMap *a = parcHashMap_Create(); + * + * PARCHashMap *copy = parcHashMap_Copy(&b); + * + * parcHashMap_Release(&b); + * parcHashMap_Release(©); + * } + * @endcode + */ +PARCHashMap *parcHashMap_Copy(const PARCHashMap *original); + +/** + * Print a human readable representation of the given `PARCHashMap`. + * + * @param [in] instance A pointer to a valid PARCHashMap instance. + * @param [in] indentation The indentation level to use for printing. + * + * Example: + * @code + * { + * PARCHashMap *a = parcHashMap_Create(); + * + * parcHashMap_Display(a, 0); + * + * parcHashMap_Release(&a); + * } + * @endcode + */ +void parcHashMap_Display(const PARCHashMap *instance, int indentation); + +/** + * Determine if two `PARCHashMap` instances are equal. + * + * The following equivalence relations on non-null `PARCHashMap` instances are maintained: * + * * It is reflexive: for any non-null reference value x, `parcHashMap_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `parcHashMap_Equals(x, y)` must return true if and only if + * `parcHashMap_Equals(y x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcHashMap_Equals(x, y)` returns true and + * `parcHashMap_Equals(y, z)` returns true, + * then `parcHashMap_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `parcHashMap_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `parcHashMap_Equals(x, NULL)` must return false. + * + * @param [in] x A pointer to a valid PARCHashMap instance. + * @param [in] y A pointer to a valid PARCHashMap instance. + * + * @return true The instances x and y are equal. + * + * Example: + * @code + * { + * PARCHashMap *a = parcHashMap_Create(); + * PARCHashMap *b = parcHashMap_Create(); + * + * if (parcHashMap_Equals(a, b)) { + * printf("Instances are equal.\n"); + * } + * + * parcHashMap_Release(&a); + * parcHashMap_Release(&b); + * } + * @endcode + * @see parcHashMap_HashCode + */ +bool parcHashMap_Equals(const PARCHashMap *x, const PARCHashMap *y); + +/** + * Returns a hash code value for the given instance. + * + * The general contract of `HashCode` is: + * + * Whenever it is invoked on the same instance more than once during an execution of an application, + * the `HashCode` function must consistently return the same value, + * provided no information used in a corresponding comparisons on the instance is modified. + * + * This value need not remain consistent from one execution of an application to another execution of the same application. + * If two instances are equal according to the {@link parcHashMap_Equals} method, + * then calling the {@link parcHashMap_HashCode} method on each of the two instances must produce the same integer result. + * + * It is not required that if two instances are unequal according to the + * {@link parcHashMap_Equals} function, + * then calling the `parcHashMap_HashCode` + * method on each of the two objects must produce distinct integer results. + * + * @param [in] instance A pointer to a valid PARCHashMap instance. + * + * @return The hashcode for the given instance. + * + * Example: + * @code + * { + * PARCHashMap *a = parcHashMap_Create(); + * + * uint32_t hashValue = parcHashMap_HashCode(buffer); + * parcHashMap_Release(&a); + * } + * @endcode + */ +PARCHashCode parcHashMap_HashCode(const PARCHashMap *instance); + +/** + * Determine if an instance of `PARCHashMap` is valid. + * + * Valid means the internal state of the type is consistent with its required current or future behaviour. + * This may include the validation of internal instances of types. + * + * @param [in] instance A pointer to a valid PARCHashMap instance. + * + * @return true The instance is valid. + * @return false The instance is not valid. + * + * Example: + * @code + * { + * PARCHashMap *a = parcHashMap_Create(); + * + * if (parcHashMap_IsValid(a)) { + * printf("Instance is valid.\n"); + * } + * + * parcHashMap_Release(&a); + * } + * @endcode + * + */ +bool parcHashMap_IsValid(const PARCHashMap *instance); + +/** + * Release a previously acquired reference to the given `PARCHashMap` instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] instancePtr A pointer to a pointer to the instance to release. + * + * Example: + * @code + * { + * PARCHashMap *a = parcHashMap_Create(); + * + * parcHashMap_Release(&a); + * } + * @endcode + */ +void parcHashMap_Release(PARCHashMap **instancePtr); + +/** + * Create a `PARCJSON` instance (representation) of the given object. + * + * @param [in] instance A pointer to a valid PARCHashMap instance. + * + * @return NULL Memory could not be allocated to contain the `PARCJSON` instance. + * @return non-NULL An allocated C string that must be deallocated via parcMemory_Deallocate(). + * + * Example: + * @code + * { + * PARCHashMap *a = parcHashMap_Create(); + * + * PARCJSON *json = parcHashMap_ToJSON(a); + * + * printf("JSON representation: %s\n", parcJSON_ToString(json)); + * parcJSON_Release(&json); + * + * parcHashMap_Release(&a); + * } + * @endcode + */ +PARCJSON *parcHashMap_ToJSON(const PARCHashMap *instance); + + +PARCBufferComposer *parcHashMap_BuildString(const PARCHashMap *hashMap, PARCBufferComposer *composer); + +/** + * Produce a null-terminated string representation of the specified `PARCHashMap`. + * + * The result must be freed by the caller via {@link parcMemory_Deallocate}. + * + * @param [in] instance A pointer to a valid PARCHashMap instance. + * + * @return NULL Cannot allocate memory. + * @return non-NULL A pointer to an allocated, null-terminated C string that must be deallocated via {@link parcMemory_Deallocate}. + * + * Example: + * @code + * { + * PARCHashMap *a = parcHashMap_Create(); + * + * char *string = parcHashMap_ToString(a); + * + * parcHashMap_Release(&a); + * + * parcMemory_Deallocate(&string); + * } + * @endcode + * + * @see parcHashMap_Display + */ +char *parcHashMap_ToString(const PARCHashMap *instance); + +/** + * Wakes up a single thread that is waiting on this object (see `parcLinkedList_Wait)`. + * If any threads are waiting on this object, one of them is chosen to be awakened. + * The choice is arbitrary and occurs at the discretion of the underlying implementation. + * + * The awakened thread will not be able to proceed until the current thread relinquishes the lock on this object. + * The awakened thread will compete in the usual manner with any other threads that might be actively + * competing to synchronize on this object; + * for example, the awakened thread enjoys no reliable privilege or disadvantage in being the next thread to lock this object. + * + * @param [in] object A pointer to a valid PARCLinkedList instance. + * + * Example: + * @code + * { + * + * parcLinkedList_Notify(object); + * } + * @endcode + */ +parcObject_ImplementNotify(parcHashMap, PARCHashMap); + +/** + * Causes the calling thread to wait until either another thread invokes the parcHashMap_Notify() function on the same object. + * * + * @param [in] object A pointer to a valid `PARCHashMap` instance. + * + * Example: + * @code + * { + * + * parcHashMap_Wait(object); + * } + * @endcode + */ +parcObject_ImplementWait(parcHashMap, PARCHashMap); + +/** + * Obtain the lock on the given `PARCHashMap` instance. + * + * If the lock is already held by another thread, this function will block. + * If the lock is aleady held by the current thread, this function will return `false`. + * + * Implementors must avoid deadlock by attempting to lock the object a second time within the same calling thread. + * + * @param [in] object A pointer to a valid `PARCHashMap` instance. + * + * @return true The lock was obtained successfully. + * @return false The lock is already held by the current thread, or the `PARCHashMap` is invalid. + * + * Example: + * @code + * { + * if (parcHashMap_Lock(object)) { + * + * } + * } + * @endcode + */ +parcObject_ImplementLock(parcHashMap, PARCHashMap); + +/** + * Try to obtain the advisory lock on the given PARCHashMap instance. + * + * Once the lock is obtained, the caller must release the lock as soon as possible. + * + * @param [in] object A pointer to a valid PARCHashMap instance. + * + * @return true The PARCHashMap is locked. + * @return false The PARCHashMap is unlocked. + * + * Example: + * @code + * { + * parcHashMap_TryLock(object); + * } + * @endcode + */ +parcObject_ImplementTryLock(parcHashMap, PARCHashMap); + +/** + * Try to unlock the advisory lock on the given `PARCHashMap` instance. + * + * @param [in] object A pointer to a valid `PARCHashMap` instance. + * + * @return true The `PARCHashMap` was locked and now is unlocked. + * @return false The `PARCHashMap` was not locked and remains unlocked. + * + * Example: + * @code + * { + * parcHashMap_Unlock(object); + * } + * @endcode + */ +parcObject_ImplementUnlock(parcHashMap, PARCHashMap); + +/** + * Determine if the advisory lock on the given `PARCHashMap` instance is locked. + * + * @param [in] object A pointer to a valid `PARCHashMap` instance. + * + * @return true The `PARCHashMap` is locked. + * @return false The `PARCHashMap` is unlocked. + * Example: + * @code + * { + * if (parcHashMap_IsLocked(object)) { + * ... + * } + * } + * @endcode + */ +parcObject_ImplementIsLocked(parcHashMap, PARCHashMap); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [in] hashMap A pointer to a valid PARCHashMap instance. + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +PARCHashMap *parcHashMap_Put(PARCHashMap *hashMap, const PARCObject *key, const PARCObject *value); + +/** + * Returns the value to which the specified key is mapped, + * or null if this map contains no mapping for the key. + * If this map contains a mapping from a key _k_ to a value _v_ such that `(key==null ? k==null : key.equals(k))`, + * then this method returns _v_; otherwise it returns null. (There can be at most one such mapping.) + * + * A return value of `NULL` does not necessarily indicate that the map contains no mapping for the key. + * It is possible that the map explicitly maps the key to `NULL`. + * Use the `parcHashMap_ContainsKey` function to distinguish these cases. + * + * @param [in] hashMap A pointer to a valid PARCHashMap instance. + * + * @return The value to which the specified key is mapped, or `NULL` if this map contains no mapping for the key + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +const PARCObject *parcHashMap_Get(const PARCHashMap *hashMap, const PARCObject *key); + +/** + * Removes the mapping for the specified key from this `PARCHashMap`, if present. + * + * @param [in] hashMap A pointer to a valid PARCHashMap instance. + * + * @return true The key existed and was removed. + * @return true The key did not exist. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +bool parcHashMap_Remove(PARCHashMap *hashMap, const PARCObject *key); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [in] hashMap A pointer to a valid PARCHashMap instance. + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +size_t parcHashMap_Size(const PARCHashMap *hashMap); + + +/** + * Computes the standard deviation of the PARCHashMap's bucket sizes from a value of 1.0 + * (as opposed to the mean) and weighs the result by in inverse of the current load + * factor. The deviation from 1.0 is used because the hash-map's max load factor is < 1.0 + * and thus the ideal average chain-length is 1.0. + * + * A result of 0.0 equates to an ideal distribution, a result of ~1.0 should represent + * a fairly normal or random distribution, and a result > 1.5 or so implies some amount + * of undesirable clumping may be happening. + * + * @param [in] hashMap A pointer to a valid PARCHashMap instance. + * + * @return The clustering number + */ +double parcHashMap_GetClusteringNumber(const PARCHashMap *hashMap); + + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [in] hashMap A pointer to a valid PARCHashMap instance. + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +bool parcHashMap_Contains(PARCHashMap *hashMap, const PARCObject *key); + +/** + * Create a new instance of PARCIterator that iterates through the values of the specified `PARCHashMap`. + * The returned value must be released via {@link parcIterator_Release}. + * + * @param [in] hashMap A pointer to a valid `PARCHashMap`. + * + * @see parcIterator_Release + * Example: + * @code + * { + * PARCIterator *iterator = parcHashMap_CreateValueIterator(list); + * + * while (parcIterator_HasNext(iterator)) { + * PARCObject *object = parcIterator_Next(iterator); + * } + * + * parcIterator_Release(&iterator); + * } + * @endcode + */ +PARCIterator *parcHashMap_CreateValueIterator(PARCHashMap *hashMap); + +/** + * Create a new instance of PARCIterator that iterates through the keys of the specified `PARCHashMap`. + * The returned value must be released via {@link parcIterator_Release}. + * + * @param [in] hashMap A pointer to a valid `PARCHashMap`. + * + * @see parcIterator_Release + * Example: + * @code + * { + * PARCIterator *iterator = parcHashMap_CreateKeyIterator(list); + * + * while (parcIterator_HasNext(iterator)) { + * PARCObject *object = parcIterator_Next(iterator); + * } + * + * parcIterator_Release(&iterator); + * } + * @endcode + */ +PARCIterator *parcHashMap_CreateKeyIterator(PARCHashMap *hashMap); +#endif diff --git a/libparc/parc/algol/parc_InputStream.c b/libparc/parc/algol/parc_InputStream.c new file mode 100755 index 00000000..ade5a46a --- /dev/null +++ b/libparc/parc/algol/parc_InputStream.c @@ -0,0 +1,57 @@ +/* + * 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 <stdio.h> + +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_InputStream.h> + +struct parc_input_stream { + void *instance; + const PARCInputStreamInterface *interface; +}; + +static void +_destroy(PARCInputStream **inputStreamPtr) +{ + PARCInputStream *inputStream = *inputStreamPtr; + parcObject_Release(&inputStream->instance); +} + +parcObject_ExtendPARCObject(PARCInputStream, _destroy, NULL, NULL, NULL, NULL, NULL, NULL); + + +PARCInputStream * +parcInputStream(void *instance, const PARCInputStreamInterface *interface) +{ + PARCInputStream *result = parcObject_CreateInstance(PARCInputStream); + result->instance = instance; + result->interface = interface; + + return result; +} + +parcObject_ImplementAcquire(parcInputStream, PARCInputStream); + +parcObject_ImplementRelease(parcInputStream, PARCInputStream); + +size_t +parcInputStream_Read(PARCInputStream *inputStream, PARCBuffer *buffer) +{ + return (inputStream->interface->Read)(inputStream->instance, buffer); +} diff --git a/libparc/parc/algol/parc_InputStream.h b/libparc/parc/algol/parc_InputStream.h new file mode 100755 index 00000000..3ab6849a --- /dev/null +++ b/libparc/parc/algol/parc_InputStream.h @@ -0,0 +1,117 @@ +/* + * 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. + */ + +/** + * @file parc_InputStream.h + * @ingroup inputoutput + * @brief Generalized Input Stream + * + * a `PARCFileInputStream` is a kind of `PARCInputStream` + * + */ +#ifndef libparc_parc_InputStream_h +#define libparc_parc_InputStream_h + +#include <parc/algol/parc_Buffer.h> + + +/** + * @typedef `PARCInputStream` + */ +struct parc_input_stream; +typedef struct parc_input_stream PARCInputStream; + +/** + * @typedef PARCInputStreamInterface + */ + +typedef struct parc_input_stream_interface { + size_t (*Read)(PARCInputStream *inputStream, PARCBuffer *buffer); + + PARCInputStream *(*Acquire)(const PARCInputStream * instance); + + void (*Release)(PARCInputStream **instancePtr); +} PARCInputStreamInterface; + +/** + * Create an instance of a `PARCInputStream` given a pointer to an instance and interface. + * + * + * @param [in] instance A pointer to a structure suitable for the given `PARCInputStreamInterface`. + * @param [in] interface A pointer to a `PARCInputStreamInterface` + * + * @return non-NULL A pointer to a valid PARCInputStream + * @return NULL Memory could not be allocated. + * + * Example: + * @code + * { + * } + * @endcode + */ +PARCInputStream *parcInputStream(void *instance, const PARCInputStreamInterface *interface); + +/** + * Read a `PARCInputStream` into a {@link PARCBuffer}. + * + * The contents of the `PARCBuffer` are filled from the current position to the limit. + * When this function returns the position is set to the end of the last successfully read byte of data. + * + * @param [in] inputStream The `PARCInputStream` to read. + * @param [in] buffer The `PARCBuffer` to fill, from the current position of the buffer to its limit. + * + * @return number of bytes read / filled. + * + * + * Example: + * @code + * <#example#> + * @endcode + */ +size_t parcInputStream_Read(PARCInputStream *inputStream, PARCBuffer *buffer); + +/** + * Acquire a new reference to an instance of `PARCInputStream`. + * + * The reference count to the instance is incremented. + * + * @param [in] instance The instance of `PARCInputStream` to which to refer. + * + * @return The same value as the input parameter @p instance + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +PARCInputStream *parcInputStream_Acquire(const PARCInputStream *instance); + +/** + * Release a `PARCInputStream` reference. + * + * Only the last invocation where the reference count is decremented to zero, + * will actually destroy the `PARCInputStream`. + * + * @param [in,out] instancePtr is a pointer to the `PARCInputStream` reference. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void parcInputStream_Release(PARCInputStream **instancePtr); +#endif // libparc_parc_InputStream_h diff --git a/libparc/parc/algol/parc_Iterator.c b/libparc/parc/algol/parc_Iterator.c new file mode 100755 index 00000000..e70a0ff9 --- /dev/null +++ b/libparc/parc/algol/parc_Iterator.c @@ -0,0 +1,159 @@ +/* + * 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 <parc/algol/parc_Iterator.h> + +struct parc_iterator { + PARCObject *object; + void *(*init)(PARCObject *); + bool (*hasNext)(PARCObject *, void *); + void *(*next)(PARCObject *, void *); + void (*remove)(PARCObject *, void **); + void *(*element)(PARCObject *, void *); + void (*fini)(PARCObject *, void *); + void (*assertValid)(const void *); + bool initialized; + void *state; +}; + +static void +_parcIterator_Destroy(PARCIterator **iteratorPtr) +{ + PARCIterator *iterator = *iteratorPtr; + + parcObject_Release(&(iterator->object)); + + (iterator->fini(iterator->object, iterator->state)); +} + +parcObject_ExtendPARCObject(PARCIterator, _parcIterator_Destroy, NULL, NULL, NULL, NULL, NULL, NULL); + +static void * +_parcIterator_Init(PARCIterator *iterator) +{ + if (iterator->init) { + iterator->state = (iterator->init)(iterator->object); + } + + if (iterator->assertValid) { + (iterator->assertValid)(iterator->state); + } + + iterator->initialized = true; + return iterator->state; +} + +static void +_parcIteratorState_AssertValid(const PARCIterator *iterator) +{ + if (iterator->assertValid) { + (iterator->assertValid)(iterator->state); + } +} + +PARCIterator * +parcIterator_Create(PARCObject *object, + void *(*init)(PARCObject *), + bool (*hasNext)(PARCObject *, void *), + void *(*next)(PARCObject *, void *), + void (*remove)(PARCObject *, void **), + void *(*element)(PARCObject *, void *), + void (*fini)(PARCObject *, void *), + void (*assertValid)(const void *)) +{ + assertNotNull(object, "PARCObject cannot be NULL."); + assertNotNull(init, "'init' function cannot be NULL."); + assertNotNull(hasNext, "'hasNext' function cannot be NULL."); + assertNotNull(next, "'next' function cannot be NULL."); + assertNotNull(element, "'element' function cannot be NULL."); + assertNotNull(fini, "'fini' function cannot be NULL."); + + PARCIterator *result = parcObject_CreateInstance(PARCIterator); + + if (result != NULL) { + result->object = parcObject_Acquire(object); + result->init = init; + result->hasNext = hasNext; + result->next = next; + result->remove = remove; + result->element = element; + result->fini = fini; + result->assertValid = assertValid; + + result->initialized = false; + _parcIterator_Init(result); + } + + return result; +} + +bool +parcIterator_IsValid(const PARCIterator *iterator) +{ + bool result = false; + + if (iterator != NULL) { + if (parcObject_IsValid(iterator)) { + result = true; + } + } + + return result; +} + +void +parcIterator_AssertValid(const PARCIterator *iterator) +{ + assertTrue(parcIterator_IsValid(iterator), "PARCIterator is not valid."); +} + +parcObject_ImplementAcquire(parcIterator, PARCIterator); + +parcObject_ImplementRelease(parcIterator, PARCIterator); + + +void * +parcIterator_Next(PARCIterator *iterator) +{ + parcIterator_OptionalAssertValid(iterator); + + iterator->state = (iterator->next)(iterator->object, iterator->state); + + _parcIteratorState_AssertValid(iterator); + + return (iterator->element)(iterator->object, iterator->state); +} + +bool +parcIterator_HasNext(const PARCIterator *iterator) +{ + parcIterator_OptionalAssertValid(iterator); + + return (iterator->hasNext)(iterator->object, iterator->state); +} + +void +parcIterator_Remove(PARCIterator *iterator) +{ + if (iterator->remove != NULL) { + (iterator->remove)(iterator->object, &iterator->state); + } + + _parcIteratorState_AssertValid(iterator); +} diff --git a/libparc/parc/algol/parc_Iterator.h b/libparc/parc/algol/parc_Iterator.h new file mode 100644 index 00000000..275a16e7 --- /dev/null +++ b/libparc/parc/algol/parc_Iterator.h @@ -0,0 +1,250 @@ +/* + * 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. + */ + +/** + * @file parc_Iterator.h + * @ingroup memory + * @brief An iterator over any kind of iteratable collection + * + * Implementations of a PARCIterator must provide the following functions, + * each of which supports the operation of the iterator. + * @code + * _TYPEIterator * PREFIX_Init(TYPE *object) + * + * bool PREFIX_Fini(TYPE *map, _TYPEIterator *state) + * + * _PARCHashMapIterator *PREFIX_Next(PARCHashMap *map, _TYPEIterator *state) + * + * void PREFIX_Remove(PARCHashMap *map, _TYPEIterator **statePtr ) + * + * bool PREFIX_HasNext(PARCHashMap *map, _TYPEIterator *state) + * + * PARCObject *PREFIX_Element(PARCHashMap *map, const _TYPEIterator *state) + * + * PARCObject *PREFIX_Element(TYPE *map, const _TYPEIterator *state) + * @endcode + * + */ +#ifndef libparc_parc_Iterator_h +#define libparc_parc_Iterator_h + +#include <stdbool.h> +#include <parc/algol/parc_Object.h> + +struct parc_iterator; +typedef struct parc_iterator PARCIterator; + +/** + * Create a new instance of `PARCIterator` + * + * Each instance must be provided pointers to functions implementing the iteration primitives for the given PARCObject. + * + * * `init` + * This function is called only when a PARCIterator is created. + * The function is expected to initialise and return a pointer to whatever internal state necessary + * to provide the subsequent operations. + * In subsequent operations of hasNext, next, getElement, and fini, this pointer is provided as a function parameter. + * + * * `hasNext` + * This function returns true if the iteration has more elements. + * + * * `next` + * Returns the next element in the iteration. + * If there are no remaining elements in the iteration, then this function must induce a trapOutOfBounds + * + * * `remove` + * Removes the element returned by the `next` function. + * + * * `getElement` + * This function is invoked only from within the `parcIterator_Next` function and + * returns the actual iteration value from the current iterator's state. + * + * * `fini` + * This function is called only when the PARCIterator is being destroyed. + * + * @param [in] object A pointer to the PARCObject that implements the underlying iteration. + * @param [in] hasNext A pointer to a function that returns true if there are more elements. + * @param [in] next A pointer to a function that returns the next element. + * @param [in] remove A pointer to a function that removes the element last returned by the @p next function. + * @param [in] getElement A pointer to a function that, given the current position, return the element at the current position. + * @param [in] fini A pointer to a function that will be invoked when the PARCIterator is finally deallocated. + * @param [in] isValid A pointer to a function that performs validation of the iterator state. + * + * @return A `PARCIterator` + */ +PARCIterator *parcIterator_Create(PARCObject *object, + void *(*init)(PARCObject *object), + bool (*hasNext)(PARCObject *object, void *state), + void *(*next)(PARCObject *object, void *state), + void (*remove)(PARCObject *, void **state), + void *(*getElement)(PARCObject *object, void *state), + void (*fini)(PARCObject *object, void *state), + void (*isValid)(const void *)); + +/** + * Determine if an instance of `PARCIterator` is valid. + * + * Valid means the internal state of the type is consistent with its required current or future behaviour. + * This may include the validation of internal instances of types. + * + * @param [in] instance A pointer to a `PARCIterator` instance. + * + * @return true The instance is valid. + * @return false The instance is not valid. + * + * Example: + * @code + * { + * PARCIterator *instance = parcIterator_Create(...); + * + * if (parcIterator_IsValid(instance)) { + * printf("Instance is valid.\n"); + * } + * } + * @endcode + */ +bool parcIterator_IsValid(const PARCIterator *instance); + +/** + * Assert that the given `PARCIterator` instance is valid. + * + * @param [in] iterator A pointer to a valid PARCIterator instance. + * + * Example: + * @code + * { + * PARCIterator *a = parcIterator_Create(...); + * + * parcIterator_AssertValid(a); + * + * printf("Instance is valid.\n"); + * + * parcIterator_Release(&b); + * } + * @endcode + */ +#ifdef PARCLibrary_DISABLE_VALIDATION +# define parcIterator_OptionalAssertValid(_instance_) +#else +# define parcIterator_OptionalAssertValid(_instance_) parcIterator_AssertValid(_instance_) +#endif +void parcIterator_AssertValid(const PARCIterator *iterator); + +/** + * Increase the number of references to a `PARCIterator`. + * + * Note that new `PARCIterator` is not created, + * only that the given `PARCIterator` reference count is incremented. + * Discard the reference by invoking `parcIterator_Release`. + * + * @param [in] instance A pointer to a `PARCIterator` instance. + * + * @return The input `PARCIterator` pointer. + * + * Example: + * @code + * { + * PARCIterator *a = parcIterator_Create(...); + * + * PARCIterator *b = parcIterator_Acquire(a); + * + * parcIterator_Release(&a); + * parcIterator_Release(&b); + * } + * @endcode + */ +PARCIterator *parcIterator_Acquire(const PARCIterator *instance); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] iteratorPtr A pointer to a pointer to the instance to release, which will be set to NULL. + * + * Example: + * @code + * { + * PARCIterator *a = parcIterator_Create(...); + * + * parcIterator_Release(&a); + * } + * @endcode + */ +void parcIterator_Release(PARCIterator **iteratorPtr); + +/** + * Return the next item in the iterated list + * + * @param [in] iterator A pointer to the instance of `PARCIterator` + * + * @return A pointer to the next item in the iterated list + * + * Example: + * @code + * { + * while (parcIterator_HasNext(iterator)) { + * void *element = parcIterator_Next(iterator); + * } + * } + * @endcode + */ +void *parcIterator_Next(PARCIterator *iterator); + +/** + * Return true if there are more items left over which to iterate. + * + * @param [in] iterator A pointer to the instance of `PARCIterator` + * + * @return True if there is are more items over which to iterate; false otherwise. + * + * Example: + * @code + * { + * while (parcIterator_HasNext(iterator)) { + * void *element = parcIterator_Next(iterator); + * } + * } + * @endcode + */ +bool parcIterator_HasNext(const PARCIterator *iterator); + +/** + * Removes from the underlying collection the last element returned by the iterator (optional operation). + * This function can be called only once per call to `parcIterator_Next`. + * The behavior of an iterator is unspecified if the underlying collection is + * modified while the iteration is in progress in any way other than by calling this method. + * + * Pointers to the element after removing it via this function may point to invalid remnants of the object. + * To avoid this, acquire a reference to the element before invoking this function. + * + * @param [in] iterator A pointer to the instance of `PARCIterator` + * + * Example: + * @code + * { + * while (parcIterator_HasNext(iterator)) { + * void *element = parcIterator_Next(iterator); + * } + * } + * @endcode + */ +void parcIterator_Remove(PARCIterator *iterator); +#endif // libparc_parc_Iterator_h diff --git a/libparc/parc/algol/parc_JSON.c b/libparc/parc/algol/parc_JSON.c new file mode 100755 index 00000000..d70a302e --- /dev/null +++ b/libparc/parc/algol/parc_JSON.c @@ -0,0 +1,404 @@ +/* + * 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 <LongBow/runtime.h> + +#include <stdio.h> +#include <ctype.h> +#include <math.h> + +#include <parc/algol/parc_JSON.h> +#include <parc/algol/parc_JSONPair.h> +#include <parc/algol/parc_JSONValue.h> +#include <parc/algol/parc_JSONParser.h> + +#include <parc/algol/parc_DisplayIndented.h> +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_Buffer.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_List.h> +#include <parc/algol/parc_ArrayList.h> +#include <parc/algol/parc_BufferComposer.h> +#include <parc/algol/parc_PathName.h> + +struct parc_json { + PARCList *members; +}; + +static void +_destroyPARCJSON(PARCJSON **jsonPtr) +{ + PARCJSON *json = *jsonPtr; + + parcList_Release(&json->members); +} + +parcObject_ExtendPARCObject(PARCJSON, _destroyPARCJSON, NULL, parcJSON_ToString, parcJSON_Equals, NULL, NULL, NULL); + +PARCJSON * +parcJSON_Create(void) +{ + PARCJSON *result = parcObject_CreateInstance(PARCJSON); + if (result != NULL) { + result->members = parcList(parcArrayList_Create((void (*)(void **))parcJSONPair_Release), PARCArrayListAsPARCList); + } + + return result; +} + +parcObject_ImplementAcquire(parcJSON, PARCJSON); + +parcObject_ImplementRelease(parcJSON, PARCJSON); + +static bool +_memberListEquals(const PARCList *x, const PARCList *y) +{ + for (size_t i = 0; i < parcList_Size(x); i++) { + PARCJSONPair *pairA = parcList_GetAtIndex(x, i); + PARCJSONPair *pairB = parcList_GetAtIndex(y, i); + if (parcJSONPair_Equals(pairA, pairB) == false) { + return false; + } + } + return true; +} + +static PARCBuffer * +_toCreatedBuffer(const PARCJSON *json, bool compact) +{ + if (json == NULL) { + return NULL; + } + PARCBufferComposer *composer = parcBufferComposer_Create(); + parcJSON_BuildString(json, composer, compact); + + PARCBuffer *result = parcBufferComposer_ProduceBuffer(composer); + parcBufferComposer_Release(&composer); + + return result; +} + +static char * +_toString(const PARCJSON *json, bool compact) +{ + PARCBuffer *tempBuffer = _toCreatedBuffer(json, compact); + + char *result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + + return result; +} + +bool +parcJSON_Equals(const PARCJSON *x, const PARCJSON *y) +{ + bool result = false; + + if (x == NULL && y == NULL) { + result = true; + } else if (x != NULL && y != NULL) { + if (parcList_Size(x->members) == parcList_Size(y->members)) { + if (_memberListEquals(x->members, y->members)) { + result = true; + } + } + } + + return result; +} + +PARCJSON * +parcJSON_Copy(const PARCJSON *src) +{ + //TODO - This is an ineffecient deep copy. The _Copy() operation needs to be implementd for all PARCJSON* types + //before we can do an effecient deep copy. + if (src == NULL) { + return NULL; + } + PARCBuffer *temp = _toCreatedBuffer(src, true); + PARCJSON *result = parcJSON_ParseBuffer(temp); + parcBuffer_Release(&temp); + + return result; +} + + +PARCHashCode +parcJSON_HashCode(const PARCJSON *json) +{ + PARCBuffer *temp = _toCreatedBuffer(json, true); + PARCHashCode result = parcBuffer_HashCode(temp); + parcBuffer_Release(&temp); + + return result; +} + +void +parcJSON_Display(const PARCJSON *json, int indentation) +{ + parcDisplayIndented_PrintLine(indentation, "PARCJSON@%p {", json); + for (size_t index = 0; index < parcList_Size(json->members); index++) { + PARCJSONPair *pair = parcList_GetAtIndex(json->members, index); + parcJSONPair_Display(pair, indentation + 1); + } + parcDisplayIndented_PrintLine(indentation, "}"); +} + +PARCJSONPair * +parcJSON_GetPairByIndex(const PARCJSON *json, size_t index) +{ + PARCJSONPair *result = NULL; + + if (parcList_Size(json->members) > index) { + result = parcList_GetAtIndex(json->members, index); + } + + return result; +} + +PARCJSONValue * +parcJSON_GetValueByIndex(const PARCJSON *json, size_t index) +{ + PARCJSONValue *result = NULL; + + if (parcList_Size(json->members) > index) { + result = parcJSONPair_GetValue(parcList_GetAtIndex(json->members, index)); + } + + return result; +} + +const PARCJSONPair * +parcJSON_GetPairByName(const PARCJSON *json, const char *name) +{ + PARCJSONPair *result = NULL; + + PARCBuffer *nameBuffer = parcBuffer_Wrap((uint8_t *) name, strlen(name), 0, strlen(name)); + for (size_t index = 0; index < parcList_Size(json->members); index++) { + PARCJSONPair *pair = parcList_GetAtIndex(json->members, index); + if (parcBuffer_Equals(nameBuffer, parcJSONPair_GetName(pair))) { + result = pair; + break; + } + } + parcBuffer_Release(&nameBuffer); + return result; +} + +PARCJSONValue * +parcJSON_GetValueByName(const PARCJSON *json, const char *name) +{ + PARCJSONValue *result = NULL; + const PARCJSONPair *pair = parcJSON_GetPairByName(json, name); + if (pair != NULL) { + result = parcJSONPair_GetValue(pair); + } + + return result; +} + +PARCList * +parcJSON_GetMembers(const PARCJSON *json) +{ + return json->members; +} + +PARCBufferComposer * +parcJSON_BuildString(const PARCJSON *json, PARCBufferComposer *composer, bool compact) +{ + parcBufferComposer_PutChar(composer, '{'); + if (!compact) { + parcBufferComposer_PutChar(composer, ' '); + } + + char *separator = ""; + for (size_t i = 0; i < parcList_Size(json->members); i++) { + parcBufferComposer_PutString(composer, separator); + parcJSONPair_BuildString(parcList_GetAtIndex(json->members, i), composer, compact); + separator = ", "; + if (compact) { + separator = ","; + } + } + + if (!compact) { + parcBufferComposer_PutChar(composer, ' '); + } + parcBufferComposer_PutChar(composer, '}'); + return composer; +} + + +char * +parcJSON_ToString(const PARCJSON *json) +{ + return _toString(json, false); +} + +char * +parcJSON_ToCompactString(const PARCJSON *json) +{ + return _toString(json, true); +} + +const PARCJSONValue * +parcJSON_GetByPathName(const PARCJSONValue *pathNode, const PARCPathName *path) +{ + for (int i = 0; i < parcPathName_Size(path); i++) { + char *name = parcPathName_GetAtIndex(path, i); + if (parcJSONValue_IsJSON(pathNode)) { + const PARCJSONPair *pair = parcJSON_GetPairByName(parcJSONValue_GetJSON(pathNode), name); + if (pair == NULL) { + pathNode = NULL; + break; + } + pathNode = parcJSONPair_GetValue(pair); + } else if (parcJSONValue_IsArray(pathNode)) { + size_t index = strtoll(name, NULL, 10); + if (index > parcJSONArray_GetLength(parcJSONValue_GetArray(pathNode))) { + pathNode = NULL; + break; + } + pathNode = parcJSONArray_GetValue(parcJSONValue_GetArray(pathNode), index); + } else { + pathNode = NULL; + break; + } + } + + return pathNode; +} + +const PARCJSONValue * +parcJSON_GetByPath(const PARCJSON *json, const char *path) +{ + PARCJSONValue *pathNode = parcJSONValue_CreateFromJSON((PARCJSON *) json); + + PARCPathName *pathName = parcPathName_Parse(path); + const PARCJSONValue *result = parcJSON_GetByPathName(pathNode, pathName); + parcPathName_Release(&pathName); + + parcJSONValue_Release(&pathNode); + return result; +} + +PARCJSON * +parcJSON_ParseString(const char *string) +{ + // The input string is read-only, therefore we can cast it here when calling the parcBuffer_WrapCString() + // to work around the fact that the function does not take a 'const' attribute. + // This function is not going to modify the input string, so the 'const' promise will be kept. + PARCBuffer *buffer = parcBuffer_WrapCString((char *) string); + + PARCJSON *result = parcJSON_ParseBuffer(buffer); + parcBuffer_Release(&buffer); + + return result; +} + +PARCJSON * +parcJSON_ParseBuffer(PARCBuffer *buffer) +{ + PARCJSON *result = NULL; + + // The input string is read-only, therefore we can cast it here when calling the parcBuffer_WrapCString() + // to work around the fact that the function does not take a 'const' attribute. + // This function is not going to modify the input string, so the 'const' promise will be kept. + + PARCJSONParser *parser = parcJSONParser_Create(buffer); + + char firstCharacter = parcJSONParser_PeekNextChar(parser); + if (firstCharacter == '{') { + PARCJSONValue *value = parcJSONValue_ObjectParser(parser); + + result = parcJSON_Acquire(parcJSONValue_GetJSON(value)); + parcJSONValue_Release(&value); + } + + parcJSONParser_Release(&parser); + + return result; +} + +PARCJSON * +parcJSON_AddPair(PARCJSON *json, PARCJSONPair *pair) +{ + parcList_Add(json->members, parcJSONPair_Acquire(pair)); + return json; +} + +PARCJSON * +parcJSON_AddValue(PARCJSON *json, const char *name, PARCJSONValue *value) +{ + PARCJSONPair *pair = parcJSONPair_CreateFromJSONValue(name, value); + parcJSON_AddPair(json, pair); + parcJSONPair_Release(&pair); + + return json; +} + +PARCJSON * +parcJSON_AddString(PARCJSON *json, const char *name, const char *value) +{ + PARCJSONPair *pair = parcJSONPair_CreateFromString(name, value); + parcJSON_AddPair(json, pair); + parcJSONPair_Release(&pair); + + return json; +} + +PARCJSON * +parcJSON_AddObject(PARCJSON *json, const char *name, PARCJSON *value) +{ + PARCJSONPair *pair = parcJSONPair_CreateFromJSON(name, value); + parcJSON_AddPair(json, pair); + parcJSONPair_Release(&pair); + + return json; +} + +PARCJSON * +parcJSON_AddInteger(PARCJSON *json, const char *name, int64_t value) +{ + PARCJSONPair *pair = parcJSONPair_CreateFromInteger(name, value); + parcJSON_AddPair(json, pair); + parcJSONPair_Release(&pair); + + return json; +} + +PARCJSON * +parcJSON_AddBoolean(PARCJSON *json, const char *name, bool value) +{ + PARCJSONPair *pair = parcJSONPair_CreateFromBoolean(name, value); + parcJSON_AddPair(json, pair); + parcJSONPair_Release(&pair); + + return json; +} + +PARCJSON * +parcJSON_AddArray(PARCJSON *json, const char *name, PARCJSONArray *value) +{ + PARCJSONPair *pair = parcJSONPair_CreateFromJSONArray(name, value); + parcJSON_AddPair(json, pair); + parcJSONPair_Release(&pair); + + return json; +} diff --git a/libparc/parc/algol/parc_JSON.h b/libparc/parc/algol/parc_JSON.h new file mode 100755 index 00000000..dc8996ca --- /dev/null +++ b/libparc/parc/algol/parc_JSON.h @@ -0,0 +1,658 @@ +/* + * 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. + */ + +/** + * @file parc_JSON.h + * @ingroup inputoutput + * @brief A complete JSON encoding and decoding library. + * + * # Parsing # + * Parse a null-terminated C string containing JSON via {@link parcJSON_ParseString}. + * + * # Printing # + * Print a JSON object via {@link parcJSON_ToString}. + * + * # Composing # + * Compose JSON objects via {@link parcJSON_Create} and add members via {@link parcJSON_Add}. + * Compose members as JSON Pairs consisting of a name and value. See functions named `parcJSONPair_Create*` + * + */ +#ifndef libparc_parc_JSON_h +#define libparc_parc_JSON_h + +#include <stdbool.h> + +struct parc_json; +typedef struct parc_json PARCJSON; + +#include <parc/algol/parc_Buffer.h> +#include <parc/algol/parc_HashCode.h> +#include <parc/algol/parc_BufferComposer.h> +#include <parc/algol/parc_PathName.h> + +#include <parc/algol/parc_List.h> +#include <parc/algol/parc_JSONPair.h> +#include <parc/algol/parc_JSONValue.h> + +/** + * Create a new JSON object. + * + * The JSON new object contains no members. + * + * @return A pointer to a `PARCJSON` instance. + * + * Example: + * @code + * { + * PARCJSON *json = parcJSON_Create(); + * ... + * parcJSONValue_Release(&json); + * } + * @endcode + * + * @see {@link parcJSON_Add} + */ +PARCJSON *parcJSON_Create(void); + +/** + * Create a deep copy of a JSON object. Call parcJSON_Release to free the object when done with it. + * + * @return A pointer to a `PARCJSON` instance. + * + * Example: + * @code + * { + * PARCJSON *jsonSrc = parcJSON_Create(); + * ... + * PARCJSON *jsonCopy = parcJSON_Copy(jsonSrc); + * ... + * parcJSONValue_Release(&jsonSrc); + * ... + * parcJSONValue_Release(&jsonCopy); + * } + * @endcode + * + */ +PARCJSON *parcJSON_Copy(const PARCJSON *src); + +/** + * Increase the number of references to a `PARCJSON` instance. + * + * Note that a new `PARCJSON` is not created, + * only that the given `PARCJSON` reference count is incremented. + * Discard the reference by invoking {@link parcJSON_Release}. + * + * @param [in] json A pointer to the original instance. + * @return The value of the input parameter @p instance. + * + * Example: + * @code + * { + * PARCJSON *x = parcJSON_Create(); + * + * PARCJSON *x2 = parcJSON_Acquire(x); + * + * parcJSON_Release(&x); + * parcJSON_Release(&x2); + * } + * @endcode + * + * @see {@link parcJSON_Release} + */ +PARCJSON *parcJSON_Acquire(const PARCJSON *json); + +/** + * Determine if two `PARCJSON` instances are equal. + * + * Two `PARCJSON` instances are equal if, and only if, + * they contain the equal members, in the same order. + * + * The following equivalence relations on non-null `PARCJSON` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, `parcJSON_Equals(x, x)` + * must return true. + * + * * It is symmetric: for any non-null reference values x and y, + * `parcJSON_Equals(x, y)` must return true if and only if + * `parcJSON_Equals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcJSON_Equals(x, y)` returns true and + * `parcJSON_Equals(y, z)` returns true, + * then `parcJSON_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple + * invocations of `parcJSON_Equals(x, y)` consistently return true or + * consistently return false. + * + * * For any non-null reference value x, `parcJSON_Equals(x, NULL)` must + * return false. + * + * @param [in] x A pointer to a `PARCJSON` instance. + * @param [in] y A pointer to a `PARCJSON` instance. + * @return true if the two `PARCJSON` instances are equal. + * + * Example: + * @code + * { + * PARCJSON *a = parcJSON_Create(); + * PARCJSON *b = parcJSON_Create(); + * + * if (parcJSON_Equals(a, b)) { + * // true + * } else { + * // false + * } + * } + * @endcode + */ +bool parcJSON_Equals(const PARCJSON *x, const PARCJSON *y); + +/** + * Add a JSON Pair to the members of a JSON Object. + * + * @param [in,out] json A pointer to a `PARCJSON` instance. + * @param [in] pair A pointer to a `PARCJSONPair` instance. + * + * @return The pointer to the `PARCJSON` instance. + * + * Example: + * @code + * { + * PARCJSON *json = parcJSON_Create(); + * + * PARCJSONPair *pair = parcJSONPair_CreateFromInteger("pi", 314159); + * parcJSON_AddPair(json, pair); + * } + * @endcode + * + * @see parcJSONPair_Create + */ +PARCJSON *parcJSON_AddPair(PARCJSON *json, PARCJSONPair *pair); + +/** + * Pretty print the given `PARCJSON` instance. + * + * @param [in] json The `PARCJSON` instance to be printed. + * @param [in] indentation The amount of indentation to prefix each line of output + * + * Example: + * @code + * { + * PARCJSON *json = parcJSON_ParseString("{ \"string\" : \"xyzzy\" }"); + * parcJSON_Display(json, 0); + * } + * @endcode + */ +void parcJSON_Display(const PARCJSON *json, int indentation); + +/** + * Get the list of members of the given `PARCJSON` instance. + * + * A new reference to the {@link PARCList} is not created. + * The caller must create a new reference, if it retains a reference to the buffer. + * + * @param [in] json A pointer to a `PARCJSON` instance. + * @return A pointer to a `PARCList` instance containing the members. + * + * Example: + * @code + * { + * PARCJSON *json = parcJSON_ParseString("{ \"string\" : \"xyzzy\" }"); + * PARCList *members = parcJSON_GetMembers(json); + * } + * @endcode + */ +PARCList *parcJSON_GetMembers(const PARCJSON *json); + +/** + * Get the PARCJSONPair at the index in the given `PARCJSON` instance. + * + * @param [in] json A pointer to a `PARCJSON` instance. + * @param [in] index The index value of the desired element. + * @return A pointer to a `PARCJSONPair` instance containing or NULL if there is nothing at the specified index. + * + * Example: + * @code + * { + * PARCJSON *json = parcJSON_ParseString("{ \"string\" : \"xyzzy\" }"); + * PARCJSONPair *pair = parcJSON_GetPairByIndex(json, 0); + * } + * @endcode + */ +PARCJSONPair *parcJSON_GetPairByIndex(const PARCJSON *json, size_t index); + +/** + * Get the PARCJSONValue at the index in the given `PARCJSON` instance. + * + * @param [in] json A pointer to a `PARCJSON` instance. + * @param [in] index The index value of the desired element. + * @return A pointer to a `PARCJSONValue` instance containing or NULL if there is nothing at the specified index. + * + * Example: + * @code + * { + * PARCJSON *json = parcJSON_ParseString("{ \"string\" : \"xyzzy\" }"); + * PARCJSONValue *pair = parcJSON_GetValueByIndex(json, 0); + * } + * @endcode + */ +PARCJSONValue *parcJSON_GetValueByIndex(const PARCJSON *json, size_t index); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] jsonPtr A pointer to a pointer to the instance to release. + * + * Example: + * @code + * { + * PARCJSON *json = parcJSON_Create(); + * + * parcJSON_Release(&json); + * } + * @endcode + */ +void parcJSON_Release(PARCJSON **jsonPtr); + +/** + * Parse a null-terminated C string into a `PARCJSON` instance. + * + * Only 8-bit characters are parsed. + * + * @param [in] string A null-terminated C string containing a well-formed JSON object. + * + * @return A pointer to a `PARCJSON` instance with one reference, or NULL if an error occurred. + * + * Example: + * @code + * { + * PARCJSON *json = parcJSON_ParseString("{ \"key\" : 1, \"array\" : [1, 2, 3] }"); + * + * parcJSON_Release(&json); + * } + * @endcode + */ +PARCJSON *parcJSON_ParseString(const char *string); + +/** + * Parse a null-terminated C string into a `PARCJSON` instance. + * + * Only 8-bit characters are parsed. + * + * @param [in] buffer A pointer to a valid PARCBuffer instance. + * + * @return A pointer to a `PARCJSON` instance with one reference, or NULL if an error occurred. + * + * Example: + * @code + * { + * PARCBufer *buffer = parcBuffer_WrapCString("{ \"key\" : 1, \"array\" : [1, 2, 3] }"); + * PARCJSON *json = parcJSON_ParseBuffer(buffer); + * + * parcBuffer_Release(&buffer); + * + * parcJSON_Release(&json); + * } + * @endcode + */ +PARCJSON *parcJSON_ParseBuffer(PARCBuffer *buffer); + +/** + * Produce a null-terminated string representation of the specified instance. + * + * The non-null result must be freed by the caller via {@link parcMemory_Deallocate}. + * + * @param [in] json A pointer to the `PARCJSON` instance. + * + * @return NULL Cannot allocate memory. + * @return non-NULL A pointer to an allocated, + * null-terminated C string that must be deallocated via {@link parcMemory_Deallocate}. + * + * Example: + * @code + * { + * PARCJSON *json = parcJSON_Create(); + * + * char *string = parcJSON_ToString(json); + * + * if (string != NULL) { + * printf("Hello: %s\n", string); + * parcMemory_Deallocate((void **)&string); + * } else { + * printf("Cannot allocate memory\n"); + * } + * + * parcJSON_Release(&json); + * } + * @endcode + * + * @see {@link parcJSONPair_BuildString} + * @see {@link parcJSONPair_Display} + */ +char *parcJSON_ToString(const PARCJSON *json); + +/** + * Produce a null-terminated compact (minimally escaped and formated) string representation of the specified instance. + * + * The non-null result must be freed by the caller via {@link parcMemory_Deallocate}. + * + * @param [in] json A pointer to the `PARCJSON` instance. + * + * @return NULL Cannot allocate memory. + * @return non-NULL A pointer to an allocated, + * null-terminated C string that must be deallocated via {@link parcMemory_Deallocate}. + * + * Example: + * @code + * { + * PARCJSON *json = parcJSON_Create(); + * + * char *string = parcJSON_ToCompactString(json); + * + * if (string != NULL) { + * printf("Hello: %s\n", string); + * parcMemory_Deallocate((void **)&string); + * } else { + * printf("Cannot allocate memory\n"); + * } + * + * parcJSON_Release(&json); + * } + * @endcode + * + * @see {@link parcJSONPair_BuildString} + * @see {@link parcJSONPair_Display} + */ +char *parcJSON_ToCompactString(const PARCJSON *json); + +/** + * Produce a PARCHashCode for the JSON object. + * + * @param [in] json A pointer to the `PARCJSON` instance. + * + * @return PARCHashCode The object's hash-code. + * + * Example: + * @code + * { + * PARCJSON *json = parcJSON_Create(); + * ... + * PARCHashCode hashCode = parcJSON_HashCode(json); + * .... + * parcJSON_Release(&json); + * } + * @endcode + * + */ +PARCHashCode parcJSON_HashCode(const PARCJSON *json); + +/** + * Get the {@link PARCJSONPair} with the given key name. + * + * @param [in] json A pointer to a `PARCJSON` instance. + * @param [in] name A null-terminated C string containing the name of the pair to return. + * + * @return A pointer to the named `PARCJSONPair`. + * + * Example: + * @code + * { + * PARCJSON *json = parcJSON_ParseString("{ \"key\" : 1, "array" : [1, 2, 3] }"); + * + * PARCJSONPair *arrayPair = parcJSON_GetPairByName(json, "array"); + * + * parcJSON_Release(&json); + * } + * @endcode + * + * @see parcJSON_Add + */ +const PARCJSONPair *parcJSON_GetPairByName(const PARCJSON *json, const char *name); + +/** + * Get the {@link PARCJSONValue} with the given key name. + * + * @param [in] json A pointer to a `PARCJSON` instance. + * @param [in] name A null-terminated C string containing the name of the pair to return. + * + * @return A pointer to the named `PARCJSONValue`. + * + * Example: + * @code + * { + * PARCJSON *json = parcJSON_ParseString("{ \"key\" : 1, "array" : [1, 2, 3] }"); + * + * PARCJSONValue *arrayValue = parcJSON_GetValueByName(json, "array"); + * + * parcJSON_Release(&json); + * } + * @endcode + * + * @see parcJSON_Add + */ +PARCJSONValue *parcJSON_GetValueByName(const PARCJSON *json, const char *name); + +/** + * Get the JSON pair named by the given '/' separated path name. + * + * Using a '/' separated list of JSON pair names, return the {@link PARCJSONPair} named by the path. + * This function currently returns the Value, not the Pair specified by the `PARCPathName` + * + * @param [in] json A pointer to a `PARCJSON` instance. + * @param [in] path A pointer to a null-terminated C string containing the full path of the `PARCJSONPair`. + * + * @return A pointer to the {@link PARCJSONValue} named by the path. + * + * Example: + * @code + * { + * PARCJSON *json = parcJSON_ParseString("{ \"key\" : 1, "array" : [1, 2, 3] }"); + * + * PARCJSONValue *key = parcJSON_GetByPath(json, "/key"); + * + * PARCJSONValue *array_1 = parcJSON_GetByPath(json, "/array/1"); + * + * parcJSON_Release(&json); + * } + * @endcode + */ +const PARCJSONValue *parcJSON_GetByPath(const PARCJSON *json, const char *path); + +/** + * Get the JSON pair named by the given {@link PARCPathName}. + * This function currently returns the Value, not the Pair specified by the `PARCPathName` + * + * @param [in] pathNode A pointer to a {@link PARCJSONValue} instance. + * @param [in] pathName A pointer to valid `PARCPathName` instance. + * + * @return A pointer to the `PARCJSONValue` named by the path. + * Example: + * @code + * { + * PARCJSON *json = parcJSON_ParseString("{ \"key\" : 1, "array" : [1, 2, 3] }"); + * + * PARCPathName *keyPath = parcPathName_Create("/key"); + * PARCPathName *array1Path = parcPathName_Create("/array/1"); + * + * PARCJSONValue *key = parcJSON_GetByPathName(json, keyPath); + * + * PARCJSONValue *array_1 = parcJSON_GetByPathName(json, array1Path); + * + * parcJSON_Release(&json); + * } + * @endcode + */ +const PARCJSONValue *parcJSON_GetByPathName(const PARCJSONValue *pathNode, const PARCPathName *pathName); + +/** + * Append a representation of the specified {@link PARCJSON} instance to the given {@link PARCBufferComposer}. + * + * @param [in] json A pointer to the `PARCJSON` instance. + * @param [in,out] composer A `PARCBufferComposer` to append to this URI segment. + * + * @return NULL Cannot allocate memory. + * @return non-NULL The given `PARCBufferComposer`. + * + * Example: + * @code + * { + * PARCBufferComposer *result = parcBufferComposer_Create(); + * + * parcJSON_BuildString(instance, result); + * + * PARCBuffer *string = parcBufferComposer_FinalizeBuffer(result); + * printf("JSON String: %s\n", parcBuffer_ToString(string)); + * parcBuffer_Release(&string); + * + * parcBufferComposer_Release(&result); + * } + * @endcode + */ +PARCBufferComposer *parcJSON_BuildString(const PARCJSON *json, PARCBufferComposer *composer, bool compact); + +/** + * Create and add a JSON string pair to a PARCJSON object. + * + * @param [in] json A pointer to a valid `PARCJSON` instance. + * @param [in] name A pointer to a nul-terminated C string containing the name of the pair. + * @param [in] value A pointer to a nul-terminated C string containing the value. + * + * @return A pointer to the updated `PARCJSON` instance. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +PARCJSON *parcJSON_AddString(PARCJSON *json, const char *name, const char *value); + +/** + * Create and add a JSON object pair to a PARCJSON object. + * + * @param [in] json A pointer to a valid `PARCJSON` instance. + * @param [in] name A pointer to a nul-terminated C string containing the name of the pair. + * @param [in] value A pointer to a valid `PARCJON` value. + * + * @return A pointer to the updated `PARCJSON` instance. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +PARCJSON *parcJSON_AddObject(PARCJSON *json, const char *name, PARCJSON *value); + +/** + * Create and add a pair with an array for the value to a PARCJSON object. + * + * @param [in] json A pointer to a valid `PARCJSON` instance. + * @param [in] name A pointer to a nul-terminated C string containing the name of the pair. + * @param [in] array A pointer to a valid `PARCJONArray` value. + * + * @return A pointer to the updated `PARCJSON` instance. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +PARCJSON *parcJSON_AddArray(PARCJSON *json, const char *name, PARCJSONArray *array); + +/** + * Create and add a pair with a PARCJSONValue for the value to a PARCJSON object. + * + * @param [in] json A pointer to a valid `PARCJSON` instance. + * @param [in] name A pointer to a nul-terminated C string containing the name of the pair. + * @param [in] value A pointer to a valid `PARCJONValue` value. + * + * @return A pointer to the updated `PARCJSON` instance. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +PARCJSON *parcJSON_AddValue(PARCJSON *json, const char *name, PARCJSONValue *value); + +/** + * Create and add an integer pair to a PARCJSON object. + * + * @param [in] json A pointer to a valid `PARCJSON` instance. + * @param [in] name A pointer to a nul-terminated C string containing the name of the pair. + * @param [in] value An integer value. + * + * @return A pointer to the updated `PARCJSON` instance. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +PARCJSON *parcJSON_AddInteger(PARCJSON *json, const char *name, int64_t value); + +/** + * Create and add a boolean pair to a PARCJSON object. + * + * @param [in] json A pointer to a valid `PARCJSON` instance. + * @param [in] name A pointer to a nul-terminated C string containing the name of the pair. + * @param [in] value An boolean value. + * + * @return A pointer to the updated `PARCJSON` instance. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +PARCJSON *parcJSON_AddBoolean(PARCJSON *json, const char *name, bool value); + +/** + * Create and add a boolean pair to a PARCJSON object. + * + * @param [in] json A pointer to a valid `PARCJSON` instance. + * @param [in] name A pointer to a nul-terminated C string containing the name of the pair. + * @param [in] value An boolean value. + * + * @return A pointer to the updated `PARCJSON` instance. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +PARCJSON *parcJSON_AddArray(PARCJSON *json, const char *name, PARCJSONArray *value); +#endif // libparc_parc_JSON_h diff --git a/libparc/parc/algol/parc_JSONArray.c b/libparc/parc/algol/parc_JSONArray.c new file mode 100755 index 00000000..87421e12 --- /dev/null +++ b/libparc/parc/algol/parc_JSONArray.c @@ -0,0 +1,185 @@ +/* + * 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 <LongBow/runtime.h> +#include <parc/algol/parc_JSONArray.h> + +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_Deque.h> +#include <parc/algol/parc_JSONValue.h> +#include <parc/algol/parc_DisplayIndented.h> + +struct parcJSONArray { + PARCDeque *array; +}; + +static void +_destroy(PARCJSONArray **arrayPtr) +{ + PARCJSONArray *array = *arrayPtr; + // Un-reference the JSONValue instances here because parcDeque doesn't (yet) acquire and release its own references. + + for (int i = 0; i < parcDeque_Size(array->array); i++) { + PARCJSONValue *value = parcDeque_GetAtIndex(array->array, i); + parcJSONValue_Release(&value); + } + parcDeque_Release(&array->array); +} + +parcObject_ExtendPARCObject(PARCJSONArray, _destroy, NULL, parcJSONArray_ToString, parcJSONArray_Equals, NULL, NULL, NULL); + +static const PARCObjectDescriptor parcArrayValue_ObjInterface = { + .destroy = (PARCObjectDestroy *) parcJSONValue_Release, + .toString = (PARCObjectToString *) parcJSONValue_ToString, + .equals = (PARCObjectEquals *) parcJSONValue_Equals +}; + +void +parcJSONArray_AssertValid(const PARCJSONArray *array) +{ + assertNotNull(array, "Must be a non-null pointer to a PARCJSONArray instance."); + assertNotNull(array->array, "Must be a non-null pointer to a PARCDeque instance."); +} + +PARCJSONArray * +parcJSONArray_Create(void) +{ + PARCJSONArray *result = parcObject_CreateInstance(PARCJSONArray); + result->array = parcDeque_CreateObjectInterface(&parcArrayValue_ObjInterface); + return result; +} + +parcObject_ImplementAcquire(parcJSONArray, PARCJSONArray); + +parcObject_ImplementRelease(parcJSONArray, PARCJSONArray); + +bool +parcJSONArray_Equals(const PARCJSONArray *x, const PARCJSONArray *y) +{ + bool result = false; + + if (x == y) { + result = true; + } else if (x == NULL || y == NULL) { + result = false; + } else { + result = parcDeque_Equals(x->array, y->array); + } + return result; +} + +PARCJSONArray * +parcJSONArray_AddValue(PARCJSONArray *array, PARCJSONValue *value) +{ + parcDeque_Append(array->array, parcJSONValue_Acquire(value)); + return array; +} + +size_t +parcJSONArray_GetLength(const PARCJSONArray *array) +{ + return parcDeque_Size(array->array); +} + +PARCJSONValue * +parcJSONArray_GetValue(const PARCJSONArray *array, size_t index) +{ + return (PARCJSONValue *) parcDeque_GetAtIndex(array->array, index); +} + +PARCBufferComposer * +parcJSONArray_BuildString(const PARCJSONArray *array, PARCBufferComposer *composer, bool compact) +{ +#ifdef hasPARCIterator + PARCIterator *iterator = parcDeque_GetIterator(array->array); + + parcBufferComposer_PutChar(composer, '['); + + char *separator = ""; + + for (i = parcIterator_Start(); i < parcIterator_Limit(iterator); i = parcIterator_Next(iterator)) { + PARCJSONValue *value = parcIterator_Get(iterator); + parcBufferComposer_PutString(composer, separator); + separator = ", "; + if (compact) { + separator = ","; + } + + parcJSONValue_BuildString(value, composer); + } + parcBufferComposer_PutChar(composer, ']'); +#else + parcBufferComposer_PutChar(composer, '['); + if (!compact) { + parcBufferComposer_PutChar(composer, ' '); + } + + char *separator = ""; + + for (int i = 0; i < parcDeque_Size(array->array); i++) { + PARCJSONValue *value = parcDeque_GetAtIndex(array->array, i); + parcBufferComposer_PutString(composer, separator); + + parcJSONValue_BuildString(value, composer, compact); + separator = ", "; + if (compact) { + separator = ","; + } + } + if (!compact) { + parcBufferComposer_PutChar(composer, ' '); + } + parcBufferComposer_PutChar(composer, ']'); +#endif + return composer; +} + +void +parcJSONArray_Display(const PARCJSONArray *array, int indentation) +{ + parcDisplayIndented_PrintLine(indentation, "PARCJSONArray@%p {", array); + parcDisplayIndented_PrintLine(indentation, "}"); +} + +static char * +_parcJSONArray_ToString(const PARCJSONArray *array, bool compact) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + + parcJSONArray_BuildString(array, composer, compact); + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + char *result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + parcBufferComposer_Release(&composer); + + return result; +} + +char * +parcJSONArray_ToString(const PARCJSONArray *array) +{ + return _parcJSONArray_ToString(array, false); +} + +char * +parcJSONArray_ToCompactString(const PARCJSONArray *array) +{ + return _parcJSONArray_ToString(array, true); +} diff --git a/libparc/parc/algol/parc_JSONArray.h b/libparc/parc/algol/parc_JSONArray.h new file mode 100755 index 00000000..32fa9817 --- /dev/null +++ b/libparc/parc/algol/parc_JSONArray.h @@ -0,0 +1,333 @@ +/* + * 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. + */ + +/** + * @file parc_JSONArray.h + * @brief A JSON Array stores an array of JSON objects. + * @ingroup inputoutput + * + */ +#ifndef libparc_parc_JSONArray_h +#define libparc_parc_JSONArray_h + +struct parcJSONArray; +typedef struct parcJSONArray PARCJSONArray; + +#include <parc/algol/parc_JSONValue.h> + +/** + * Create an empty `PARCJSONArray` instance. + * + * @return A pointer to an empty `PARCJSONArray` instance. + * + * Example: + * @code + * { + * PARCJSONArray *x = parcJSONArray_Create(); + * parcJSONArray_Release(&x); + * } + * @endcode + * + * @see parcJSONArray_AddValue + */ +PARCJSONArray *parcJSONArray_Create(void); + +/** + * Increase the number of references to a `PARCJSONArray`. + * + * Note that new `PARCJSONArray` is not created, + * only that the given `PARCJSONArray` reference count is incremented. + * Discard the reference by invoking {@link parcJSONArray_Release}. + * + * @param [in] array A pointer to a `PARCJSONArray` instance. + * + * @return The input `PARCJSONArray` pointer. + * + * Example: + * @code + * { + * PARCJSONArray *x = parcJSONArray_Create(); + * + * PARCJSONArray *x_2 = parcJSONArray_Acquire(x); + * + * parcJSONArray_Release(&x); + * parcJSONArray_Release(&x_2); + * } + * @endcode + */ +PARCJSONArray *parcJSONArray_Acquire(const PARCJSONArray *array); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] arrayPtr A pointer to a pointer to the instance of `PARCJSONArray` to release. + * + * Example: + * @code + * { + * PARCJSONArray *x = parcJSONArray_Create(); + * + * parcJSONArray_Release(&x); + * } + * @endcode + */ +void parcJSONArray_Release(PARCJSONArray **arrayPtr); + +/** + * Assert that an instance of `PARCJSONArray` is valid. + * + * If the instance is not valid, terminate via {@link trapIllegalValue} + * + * Valid means the internal state of the type is consistent with its required current or future behaviour. + * This may include the validation of internal instances of types. + * + * @param [in] array A pointer to a `PARCJSONArray` instance. + */ +void parcJSONArray_AssertValid(const PARCJSONArray *array); + +/** + * Determine if two `PARCJSONArray` instances are equal. + * + * The following equivalence relations on non-null `PARCJSONArray` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, `parcJSONArray_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `parcJSONArray_Equals(x, y)` must return true if and only if + * `parcJSONArray_Equals(y x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcJSONArray_Equals(x, y)` returns true and + * `parcJSONArray_Equals(y, z)` returns true, + * then `parcJSONArray_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `parcJSONArray_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `parcJSONArray_Equals(x, NULL)` must return false. + * + * @param [in] x A pointer to a `PARCJSONArray` instance. + * @param [in] y A pointer to a `PARCJSONArray` instance. + * + * @return true `PARCJSONArray` x and y are equal. + * @return false `PARCJSONArray` x and y are not equal. + * + * Example: + * @code + * { + * PARCJSONArray *x = parcJSONArray_Create(); + * PARCJSONArray *y = parcJSONArray_Create(); + * + * if (parcJSONArray_Equals(x, y)) { + * printf("Arrays are equal.\n"); + * } else { + * printf("Arrays are NOT equal.\n"); + * } + * + * parcJSONArray_Release(&x); + * parcJSONArray_Release(&y); + * } + * @endcode + */ +bool parcJSONArray_Equals(const PARCJSONArray *x, const PARCJSONArray *y); + +/** + * Add {@link PARCJSONValue} instance to the given `PARCJSONArray`. + * + * A new reference to the `PARCJSONValue` is acquired by this call. + * + * @param [in,out] array A pointer to a `PARCJSONArray` instance. + * @param [in] value A pointer to a `PARCJSONValue` instance. + * + * @return A pointer to the @p array. + * + * Example: + * @code + * { + * PARCJSONArray *array = parcJSONArray_Create(); + * + * PARCJSONValue *value = parcJSONValue_CreateFromInteger(31415); + * parcJSONArray_AddValue(array, value); + * parcJSONValue_Release(&value); + * + * parcJSONArray_Release(&array); + * } + * @endcode + * + */ +PARCJSONArray *parcJSONArray_AddValue(PARCJSONArray *array, PARCJSONValue *value); + +/** + * Get the length of the given `PARCJSONArray` instance. + * + * @param [in] array A pointer to a `PARCJSONArray` instance. + * + * @return The number of elements in the array. + * + * Example: + * @code + * { + * PARCJSONArray *array = parcJSONArray_Create(); + * + * PARCJSONValue *value = parcJSONValue_CreateFromInteger(31415); + * parcJSONArray_AddValue(array, value); + * parcJSONValue_Release(&value); + * + * parcJSONValue_GetLength(array); + * + * parcJSONArray_Release(&array); + * } + * @endcode + */ +size_t parcJSONArray_GetLength(const PARCJSONArray *array); + +/** + * Get the {@link PARCJSONValue} stored at the given index in the `PARCJSONArray`. + * A new reference is not acquired. + * The caller must acquire its own reference if needed. + * + * @param [in] array A pointer to a `PARCJSONArray` instance. + * @param [in] index The index of the `PARCJSONValue` to get. + * + * @return The pointer to the requested `PARCJSONValue`, or NULL if the index exceeded the length of the array. + * + * Example: + * @code + * { + * PARCJSONArray *array = parcJSONArray_Create(); + * + * PARCJSONValue *value = parcJSONValue_CreateFromInteger(31415); + * parcJSONArray_AddValue(array, value); + * parcJSONValue_Release(&value); + * + * PARCJSONValue *actualValue = parcJSONValue_GetValue(array, 0); + * + * parcJSONArray_Release(&array); + * } + * @endcode + */ +PARCJSONValue *parcJSONArray_GetValue(const PARCJSONArray *array, size_t index); + +/** + * Append a representation of the specified `PARCJSONArray` instance to the given + * {@link PARCBufferComposer}. + * + * @param [in] array A pointer to the `PARCJSONArray` instance. + * @param [in,out] composer A pointer to the `PARCBufferComposer` instance. + * + * @return NULL Cannot allocate memory. + * @return non-NULL The given `PARCBufferComposer`. + * + * Example: + * @code + * { + * PARCBufferComposer *result = parcBufferComposer_Create(); + * + * parcJSONArray_BuildString(instance, result); + * + * PARCBuffer *string = parcBufferComposer_FinalizeBuffer(result); + * printf("JSON Array: %s\n", parcBuffer_ToString(string)); + * parcBuffer_Release(&string); + * + * parcBufferComposer_Release(&result); + * } + * @endcode + */ +PARCBufferComposer *parcJSONArray_BuildString(const PARCJSONArray *array, PARCBufferComposer *composer, bool compact); + +/** + * Produce a null-terminated string representation of the specified instance. + * + * The result must be freed by the caller via {@link parcMemory_Deallocate}. + * + * @param [in] array A pointer to the instance. + * + * @return NULL Cannot allocate memory. + * @return non-NULL A pointer to an allocated, null-terminated C string that must be deallocated via {@link parcMemory_Deallocate}. + * + * Example: + * @code + * { + * PARCJSONArray *array = parcJSONArray_Create(); + * + * PARCJSONValue *value = parcJSONValue_CreateFromInteger(31415); + * parcJSONArray_AddValue(array, value); + * parcJSONValue_Release(&value); + * + * const char *string = parcJSONValue_ToString(array); + * + * parcMemory_Deallocate((void **) &string); + * + * parcJSONArray_Release(&array); + * } + * @endcode + * + * @see parcJSONArray_Display + */ +char *parcJSONArray_ToString(const PARCJSONArray *array); + +/** + * Produce a null-terminated compact string representation of the specified instance. + * + * The result must be freed by the caller via {@link parcMemory_Deallocate}. + * + * @param [in] array A pointer to the instance. + * + * @return NULL Cannot allocate memory. + * @return non-NULL A pointer to an allocated, null-terminated C string that must be deallocated via {@link parcMemory_Deallocate}. + * + * Example: + * @code + * { + * PARCJSONArray *array = parcJSONArray_Create(); + * + * PARCJSONValue *value = parcJSONValue_CreateFromInteger(31415); + * parcJSONArray_AddValue(array, value); + * parcJSONValue_Release(&value); + * + * const char *string = parcJSONValue_ToCompactString(array); + * + * parcMemory_Deallocate((void **) &string); + * + * parcJSONArray_Release(&array); + * } + * @endcode + * + * @see parcJSONArray_Display + */ +char *parcJSONArray_ToCompactString(const PARCJSONArray *array); + +/** + * Pretty print the given `PARCJSONArray` instance. + * + * @param [in] array The `PARCJSONArray` instance to be printed. + * @param [in] indentation The amount of indentation to prefix each line of output + * + * Example: + * @code + * { + * PARCJSONArray *array = parcJSON_ParseString("{ \"string\" : \"xyzzy\" }"); + * parcJSONArray_Display(array, 0); + * } + * @endcode + */ +void parcJSONArray_Display(const PARCJSONArray *array, int indentation); +#endif // libparc_parc_JSONArray_h diff --git a/libparc/parc/algol/parc_JSONPair.c b/libparc/parc/algol/parc_JSONPair.c new file mode 100755 index 00000000..a5df700f --- /dev/null +++ b/libparc/parc/algol/parc_JSONPair.c @@ -0,0 +1,267 @@ +/* + * 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. + */ + +/** + * This does not properly implement the equals contract because the JSON object is stored in a PARCArrayList, + * which cannot compare elements other than by equal memory address. + * + */ +#include <config.h> + +#include <LongBow/runtime.h> + +#include <stdio.h> +#include <ctype.h> +#include <math.h> + +#include <parc/algol/parc_JSON.h> +#include <parc/algol/parc_JSONPair.h> +#include <parc/algol/parc_JSONValue.h> +#include <parc/algol/parc_JSONParser.h> + +#include <parc/algol/parc_DisplayIndented.h> +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_Buffer.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_List.h> +#include <parc/algol/parc_ArrayList.h> +#include <parc/algol/parc_BufferComposer.h> + +struct parcJSONPair { + PARCBuffer *name; + PARCJSONValue *value; +}; + +static void +_destroyJSONPair(PARCJSONPair **pairPtr) +{ + if (pairPtr != NULL) { + PARCJSONPair *pair = *pairPtr; + assertNotNull(pair, "Parameter must be a non-null pointer to a valid PARCJSONPair."); + parcBuffer_Release(&pair->name); + parcJSONValue_Release(&pair->value); + } +} + +parcObject_ExtendPARCObject(PARCJSONPair, _destroyJSONPair, NULL, NULL, NULL, NULL, NULL, NULL); + +static PARCJSONPair * +_createJSONPair(void) +{ + return parcObject_CreateInstance(PARCJSONPair); +} + +PARCJSONPair * +parcJSONPair_Create(const PARCBuffer *name, PARCJSONValue *value) +{ + PARCJSONPair *result = _createJSONPair(); + if (result != NULL) { + result->name = parcBuffer_Acquire(name); + result->value = parcJSONValue_Acquire(value); + } + + return result; +} + +parcObject_ImplementAcquire(parcJSONPair, PARCJSONPair); + +parcObject_ImplementRelease(parcJSONPair, PARCJSONPair); + +void +parcJSONPair_Display(const PARCJSONPair *pair, int indentation) +{ + parcDisplayIndented_PrintLine(indentation, "PARCJSONPair@%p {", pair); + parcBuffer_Display(pair->name, indentation + 1); + parcJSONValue_Display(pair->value, indentation + 1); + parcDisplayIndented_PrintLine(indentation, "}"); +} + +PARCBuffer * +parcJSONPair_GetName(const PARCJSONPair *pair) +{ + return pair->name; +} + +PARCJSONValue * +parcJSONPair_GetValue(const PARCJSONPair *pair) +{ + return pair->value; +} + +PARCJSONPair * +parcJSONPair_CreateFromJSONValue(const char *name, PARCJSONValue *value) +{ + PARCBuffer *nameBuffer = parcBuffer_AllocateCString(name); + PARCJSONPair *result = parcJSONPair_Create(nameBuffer, value); + parcBuffer_Release(&nameBuffer); + + return result; +} + +PARCJSONPair * +parcJSONPair_CreateFromString(const char *name, const char *value) +{ + PARCBuffer *nameBuffer = parcBuffer_AllocateCString(name); + + PARCBuffer *valueBuffer = parcBuffer_AllocateCString(value); + + PARCJSONValue *jsonValue = parcJSONValue_CreateFromString(valueBuffer); + parcBuffer_Release(&valueBuffer); + + PARCJSONPair *result = parcJSONPair_Create(nameBuffer, jsonValue); + parcBuffer_Release(&nameBuffer); + parcJSONValue_Release(&jsonValue); + + return result; +} + +PARCJSONPair * +parcJSONPair_CreateFromNULL(const char *name) +{ + PARCBuffer *nameBuffer = parcBuffer_AllocateCString(name); + PARCJSONValue *jsonValue = parcJSONValue_CreateFromNULL(); + PARCJSONPair *pair = parcJSONPair_Create(nameBuffer, jsonValue); + parcBuffer_Release(&nameBuffer); + parcJSONValue_Release(&jsonValue); + + return pair; +} + +PARCJSONPair * +parcJSONPair_CreateFromBoolean(const char *name, bool value) +{ + PARCBuffer *nameBuffer = parcBuffer_AllocateCString(name); + PARCJSONValue *jsonValue = parcJSONValue_CreateFromBoolean(value); + PARCJSONPair *pair = parcJSONPair_Create(nameBuffer, jsonValue); + parcBuffer_Release(&nameBuffer); + parcJSONValue_Release(&jsonValue); + + return pair; +} + +PARCJSONPair * +parcJSONPair_CreateFromInteger(const char *name, int64_t value) +{ + PARCBuffer *nameBuffer = parcBuffer_AllocateCString(name); + PARCJSONValue *jsonValue = parcJSONValue_CreateFromInteger(value); + PARCJSONPair *pair = parcJSONPair_Create(nameBuffer, jsonValue); + parcBuffer_Release(&nameBuffer); + parcJSONValue_Release(&jsonValue); + + return pair; +} + +PARCJSONPair * +parcJSONPair_CreateFromDouble(const char *name, double value) +{ + PARCBuffer *nameBuffer = parcBuffer_AllocateCString(name); + PARCJSONValue *jsonValue = parcJSONValue_CreateFromFloat(value); + PARCJSONPair *pair = parcJSONPair_Create(nameBuffer, jsonValue); + parcBuffer_Release(&nameBuffer); + parcJSONValue_Release(&jsonValue); + + return pair; +} + +PARCJSONPair * +parcJSONPair_CreateFromJSONArray(const char *name, PARCJSONArray *value) +{ + PARCBuffer *nameBuffer = parcBuffer_AllocateCString(name); + PARCJSONValue *jsonValue = parcJSONValue_CreateFromJSONArray(value); + PARCJSONPair *pair = parcJSONPair_Create(nameBuffer, jsonValue); + parcBuffer_Release(&nameBuffer); + parcJSONValue_Release(&jsonValue); + + return pair; +} + +PARCJSONPair * +parcJSONPair_CreateFromJSON(const char *name, PARCJSON *value) +{ + PARCBuffer *nameBuffer = parcBuffer_AllocateCString(name); + PARCJSONValue *jsonValue = parcJSONValue_CreateFromJSON(value); + PARCJSONPair *pair = parcJSONPair_Create(nameBuffer, jsonValue); + parcBuffer_Release(&nameBuffer); + parcJSONValue_Release(&jsonValue); + + return pair; +} + +bool +parcJSONPair_Equals(const PARCJSONPair *objA, const PARCJSONPair *objB) +{ + if (objA == NULL && objB == NULL) { + return true; + } else if (objA != NULL && objB != NULL) { + if (parcBuffer_Equals(objA->name, objB->name)) { + if (parcJSONValue_Equals(objA->value, objB->value)) { + return true; + } + } + } + + return false; +} + +PARCBufferComposer * +parcJSONPair_BuildString(const PARCJSONPair *pair, PARCBufferComposer *composer, bool compact) +{ + parcBufferComposer_PutUint8(composer, '"'); + + parcBufferComposer_PutBuffer(composer, pair->name); + parcBuffer_Rewind(pair->name); + if (compact) { + parcBufferComposer_PutString(composer, "\":"); + } else { + parcBufferComposer_PutString(composer, "\" : "); + } + parcJSONValue_BuildString(pair->value, composer, compact); + + return composer; +} + +char * +parcJSONPair_ToString(const PARCJSONPair *pair) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + + parcJSONPair_BuildString(pair, composer, false); + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + char *result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + parcBufferComposer_Release(&composer); + + return result; +} + +PARCJSONPair * +parcJSONPair_Parser(PARCJSONParser *parser) +{ + PARCJSONPair *result = NULL; + + // This makes an unnecessary copy. I think this could just be a buffer slice. + PARCBuffer *name = parcJSONParser_ParseString(parser); + char c = parcJSONParser_NextChar(parser); + if (c == ':') { + PARCJSONValue *value = parcJSONValue_Parser(parser); + if (value != NULL) { + result = parcJSONPair_Create(name, value); + parcJSONValue_Release(&value); + } + } + parcBuffer_Release(&name); + + return result; +} diff --git a/libparc/parc/algol/parc_JSONPair.h b/libparc/parc/algol/parc_JSONPair.h new file mode 100755 index 00000000..dff8485b --- /dev/null +++ b/libparc/parc/algol/parc_JSONPair.h @@ -0,0 +1,486 @@ +/* + * 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. + */ + +/** + * @file parc_JSON.h + * @ingroup inputoutput + * @brief JSON A JSON pair consists of a name and a value separated by a colon. + * + */ +#ifndef libparc_parc_JSONPair_h +#define libparc_parc_JSONPair_h + +#include <stdbool.h> + +struct parcJSONPair; +typedef struct parcJSONPair PARCJSONPair; + +#include <parc/algol/parc_Buffer.h> +#include <parc/algol/parc_BufferComposer.h> +#include <parc/algol/parc_List.h> + +#include <parc/algol/parc_JSONArray.h> +#include <parc/algol/parc_JSONValue.h> +#include <parc/algol/parc_JSONParser.h> + +/** + * Create a new JSON Pair + * + * @param [in] name A pointer to a {@link PARCBuffer} instance containing the name for the JSON Pair. + * @param [in] value A pointer to a {@link PARCJSONValue} instance containing the value for the JSON Pair. + * @return A pointer to a new `PARCJSONPair`, or NULL if an error occured. + * + * Example: + * @code + * { + * PARCBuffer *name = parcBuffer_AllocateCString("myname"); + * PARCJSONValue *value = parcJSONValue_CreateFromInteger(31415); + * PARCJSONPair *pair = parcJSONPair_Create(name, value); + * + * parcJSONPair_Release(&pair); + * } + * @endcode + * + * @see {@link parcJSONPair_CreateFromString} + * @see {@link parcJSONPair_CreateFromNULL} + * @see {@link parcJSONPair_CreateFromBoolean} + * @see {@link parcJSONPair_CreateFromInteger} + * @see {@link parcJSONPair_CreateFromDouble} + * @see {@link parcJSONPair_CreateFromJSONArray} + * @see {@link parcJSONPair_CreateFromJSON} + */ +PARCJSONPair *parcJSONPair_Create(const PARCBuffer *name, PARCJSONValue *value); + +/** + * Create a `PARCJSONPair` consisting of the given name and value represented as null-terminated C strings. + * + * @param [in] name A pointer to a null-terminated C string for the name of the `PARCJSONPair`. + * @param [in] value A pointer to a null-terminated C string for the value of the `PARCJSONPair`. + * + * @return A pointer to a `PARCJSONPair` instance, or NULL if an error occurred. + * + * Example: + * @code + * { + * PARCJSONPair *pair = parcJSONPair_CreateFromString("name", "value"); + * + * parcJSONPair_Release(&pair); + * } + * @endcode + * + * @see parcJSONPair_Create + */ +PARCJSONPair *parcJSONPair_CreateFromString(const char *name, const char *value); + +/** + * Create a `PARCJSONPair` consisting of the given name and `PARCJSONValue`. + * + * @param [in] name A pointer to a null-terminated C string for the name of the `PARCJSONPair`. + * @param [in] value A pointer to a `PARCJSONValue` for the value of the `PARCJSONPair`. + * + * @return A pointer to a `PARCJSONPair` instance, or NULL if an error occurred. + * + * Example: + * @code + * { + * PARCJSONPair *pair = parcJSONPair_CreateFromJSONValue("name", "value"); + * + * parcJSONPair_Release(&pair); + * } + * @endcode + * + * @see parcJSONPair_Create + */ +PARCJSONPair *parcJSONPair_CreateFromJSONValue(const char *name, PARCJSONValue *value); + +/** + * Create a `PARCJSONPair` consisting of the given name and the null value. + * + * @param [in] name A pointer to a null-terminated C string for the name of the `PARCJSONPair`. + * + * @return A pointer to a `PARCJSONPair` instance, or NULL if an error occurred. + * + * Example: + * @code + * { + * PARCJSONPair *pair = parcJSONPair_CreateFromNULL("name"); + * + * parcJSONPair_Release(&pair); + * } + * @endcode + * + * @see parcJSONPair_Create + */ +PARCJSONPair *parcJSONPair_CreateFromNULL(const char *name); + +/** + * Create a `PARCJSONPair` consisting of the given name and a JSON boolean of true or false as the value. + * + * @param [in] name A pointer to a null-terminated C string for the name of the `PARCJSONPair`. + * @param [in] value Either `true` or `false`. + * + * @return A pointer to a `PARCJSONPair` instance, or NULL if an error occurred. + * + * Example: + * @code + * { + * PARCJSONPair *pair = parcJSONPair_CreateFromBoolean("name", true); + * + * parcJSONPair_Release(&pair); + * } + * @endcode + * + * @see parcJSONPair_Create + */ +PARCJSONPair *parcJSONPair_CreateFromBoolean(const char *name, bool value); + +/** + * Create a `PARCJSONPair` consisting of the given name and a JSON Integer. + * + * @param [in] name A pointer to a null-terminated C string for the name of the `PARCJSONPair`. + * @param [in] value An integer value. + * + * @return A pointer to a `PARCJSONPair` instance, or NULL if an error occurred. + * + * Example: + * @code + * { + * PARCJSONPair *pair = parcJSONPair_CreateFromInteger("name", 314159); + * + * parcJSONPair_Release(&pair); + * } + * @endcode + * + * @see parcJSONPair_Create + */ +PARCJSONPair *parcJSONPair_CreateFromInteger(const char *name, int64_t value); + +/** + * Create a `PARCJSONPair` consisting of the given name and a JSON floating point value. + * + * @param [in] name A pointer to a null-terminated C string for the name of the `PARCJSONPair`. + * @param [in] value A double value. + * + * @return A pointer to a `PARCJSONPair` instance, or NULL if an error occurred. + * + * Example: + * @code + * { + * PARCJSONPair *pair = parcJSONPair_CreateFromDouble("name", 3.14159); + * + * parcJSONPair_Release(&pair); + * } + * @endcode + * + * @see parcJSONPair_Create + */ +PARCJSONPair *parcJSONPair_CreateFromDouble(const char *name, double value); + +/** + * Create a `PARCJSONPair` consisting of the given name and a JSON Array. + * + * @param [in] name A pointer to a null-terminated C string for the name of the `PARCJSONPair`. + * @param [in] value A pointer to a {@link PARCJSONArray} instance for the value. + * + * @return A pointer to a `PARCJSONPair` instance, or NULL if an error occurred. + * + * Example: + * @code + * { + * PARCJSONArray *array = parcJSONArray_Create(); + * PARCJSONPair *pair = parcJSONPair_CreateFromJSONArray("name", array); + * parcJSONArray_Release(&array); + * + * parcJSONPair_Release(&pair); + * } + * @endcode + * + * @see parcJSONPair_Create + */ +PARCJSONPair *parcJSONPair_CreateFromJSONArray(const char *name, PARCJSONArray *value); + +/** + * Create a `PARCJSONPair` consisting of the given name and a JSON Object. + * + * @param [in] name A pointer to a null-terminated C string for the name of the `PARCJSONPair`. + * @param [in] value A pointer to a {@link PARCJSON} instance. + * + * @return A pointer to a `PARCJSONPair` instance, or NULL if an error occurred. + * + * Example: + * @code + * { + * PARCJSON *json parcJSON_Create(); + * PARCJSONPair *pair = parcJSONPair_CreateFromJSON("name", json); + * parcJSON_Release(&json); + * + * parcJSONPair_Release(&pair); + * } + * @endcode + * + * @see parcJSONPair_Create + */ +PARCJSONPair *parcJSONPair_CreateFromJSON(const char *name, PARCJSON *value); + +/** + * Increase the number of references to a `PARCJSONPair` instance. + * + * A new instance is not created, + * only that the given instance's reference count is incremented. + * Discard the acquired reference by invoking {@link parcJSONPair_Release}. + * + * @param [in] pair A pointer to a `PARCJSONPair` instance. + * @return A pointer to the original instance. + * + * Example: + * @code + * { + * PARCJSONPair *pair = parcJSONPair_CreateFromDouble("name", 3.14159); + * + * PARJSON *reference = parcJSONPair_Acquire(pair); + * + * parcJSONPair_Release(&pair); + * parcJSONPair_Release(&reference); + * } + * @endcode + */ +PARCJSONPair *parcJSONPair_Acquire(const PARCJSONPair *pair); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * The contents of the deallocated memory used for the PARC object are undefined. + * Do not reference the object after the last release. + * + * @param [in,out] pairPtr A pointer to a pointer to the `PARCJSONPair` instance to release. + * + * Example: + * @code + * { + * PARCJSONPair *pair = parcJSONPair_CreateFromDouble("name", 3.14159); + * + * parcJSONPair_Release(&pair); + * } + * @endcode + */ +void parcJSONPair_Release(PARCJSONPair **pairPtr); + +/** + * Get the {@link PARCBuffer} containing the name of the given `PARCJSONPair`. + * + * A new reference to the `PARCBuffer` is not created. + * The caller must create a new reference, if it retains a reference to the buffer. + * + * @param [in] pair A pointer to a `PARCJSONPair` instance. + * + * @return A pointer to a `PARCBuffer` instance. + * + * Example: + * @code + * { + * PARCJSONPair *pair = parcJSONPair_CreateFromDouble("name", 3.14159); + * + * const char *name = parcJSONPair_GetName(pair); + * parcJSONPair_Release(&pair); + * } + * @endcode + * + * @see parcJSONPair_Create + */ +PARCBuffer *parcJSONPair_GetName(const PARCJSONPair *pair); + +/** + * Print a human readable representation of the given `PARCJSONPair`. + * + * @param [in] pair A pointer to the instance to display. + * @param [in] indentation The level of indentation to use to pretty-print the output. + * + * Example: + * @code + * { + * PARCJSONPair *pair = parcJSONPair_CreateFromDouble("name", 3.14159); + * parcJSONPair_Display(pair, 0); + * parcJSONPair_Release(&pair); + * } + * @endcode + * + */ +void parcJSONPair_Display(const PARCJSONPair *pair, int indentation); + +/** + * Get the {@link PARCJSONValue} containing the value of the given `PARCJSONPair`. + * + * A new reference to the `PARCJSONValue` is not created. + * The caller must create a new reference, if it retains a reference to the value instance. + * + * @param [in] pair A pointer to a `PARCJSONPair` instance. + * + * @return A pointer to a `PARCJSONValue` instance. + * + * Example: + * @code + * { + * PARCJSONPair *pair = parcJSONPair_CreateFromDouble("name", 3.14159); + * PARCJSONValue *value = parcJSONPair_GetValue(pair); + * parcJSONPair_Release(&pair); + * } + * @endcode + * + * @see parcJSONPair_Create + */ +PARCJSONValue *parcJSONPair_GetValue(const PARCJSONPair *pair); + +/** + * Determine if two `PARCJSONPair` instances are equal. + * + * Two `PARCJSONPair` instances are equal if, and only if, their names and values are equal. + * + * The following equivalence relations on non-null `PARCJSONPair` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, `parcJSONPair_Equals(x, x)` + * must return true. + * + * * It is symmetric: for any non-null reference values x and y, + * `parcJSONPair_Equals(x, y)` must return true if and only if + * `parcJSONPair_Equals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcJSONPair_Equals(x, y)` returns true and + * `parcJSONPair_Equals(y, z)` returns true, + * then `parcJSONPair_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple + * invocations of `parcJSONPair_Equals(x, y)` consistently return true or + * consistently return false. + * + * * For any non-null reference value x, `parcJSONPair_Equals(x, NULL)` must + * return false. + * + * @param [in] x A pointer to a `PARCJSONPair` instance. + * @param [in] y A pointer to a `PARCJSONPair` instance. + * @return true if the two `PARCJSONPair` instances are equal. + * + * Example: + * @code + * { + * PARCJSONPair *a = parcJSONPair_Equals(); + * PARCJSONPair *b = parcJSONPair_Equals(); + * + * if (parcJSONPair_Equals(a, b)) { + * // true + * } else { + * // false + * } + * } + * @endcode + */ +bool parcJSONPair_Equals(const PARCJSONPair *x, const PARCJSONPair *y); + +/** + * Produce a null-terminated string representation of the specified instance of `PARCJSONPair`. + * + * The non-null result must be freed by the caller via {@link parcMemory_Deallocate}. + * + * @param [in] pair A pointer to the `PARCJSONPair` instance. + * + * @return NULL Cannot allocate memory. + * @return non-NULL A pointer to an allocated, + * null-terminated C string that must be deallocated via {@link parcMemory_Deallocate}. + * + * Example: + * @code + * { + * PARCJSONValue *value = parcJSONValue_CreateFromInteger(123456); + * PARCJSONPair *instance = parcJSONPair_Create(parcBuffer_Wrap("Hello", 5, 0, 5), value); + * parcJSONValue_Release(&value); + * + * char *string = parcJSONPair_ToString(instance); + * + * if (string != NULL) { + * printf("%s\n", string); + * parcMemory_Deallocate((void **)&string); + * } else { + * printf("Cannot allocate memory\n"); + * } + * + * parcJSONPair_Release(&instance); + * } + * @endcode + * + * @see parcJSONPair_BuildString + * @see parcJSONPair_Display + */ +char *parcJSONPair_ToString(const PARCJSONPair *pair); + +/** + * Append a representation of the specified instance to the given + * {@link PARCBufferComposer}. + * + * @param [in] pair A pointer to the `PARCJSONPair` instance. + * @param [in,out] composer A pointer to the `PARCBufferComposer` instance. + * + * @return NULL Cannot allocate memory. + * @return non-NULL The given `PARCBufferComposer`. + * + * Example: + * @code + * { + * PARCBufferComposer *result = parcBufferComposer_Create(); + * + * parcJSONPair_BuildString(instance, result); + * + * PARCBuffer *string = parcBufferComposer_FinalizeBuffer(result); + * printf("Hello: %s\n", parcBuffer_ToString(string)); + * parcBuffer_Release(&string); + * + * parcBufferComposer_Release(&result); + * } + * @endcode + */ +PARCBufferComposer *parcJSONPair_BuildString(const PARCJSONPair *pair, PARCBufferComposer *composer, bool compact); + +/** + * Parse a complete JSON pair + * + * A pair consists of a name and a value separated by a colon. + * + * @param [in] parser A pointer to a {@link PARCJSONParser} instance. + * + * @return non-NULL A pointer to a valid `PARCJSONPair` + * @return NULL An error occurred. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_AllocateCString("\"name\" : \"value\""); + * + * PARCJSONParser *parser = parcJSONParser_Create(buffer); + * PARCJSONPair *pair = parcJSONPair_Parser(parser); + * + * parcJSONPair_Release(&pair); + * parcJSONParser_Release(&parser); + * parcBuffer_Release(&buffer); + * } + * @endcode + */ +PARCJSONPair *parcJSONPair_Parser(PARCJSONParser *parser); +#endif // libparc_parc_JSONPair_h diff --git a/libparc/parc/algol/parc_JSONParser.c b/libparc/parc/algol/parc_JSONParser.c new file mode 100755 index 00000000..a0022597 --- /dev/null +++ b/libparc/parc/algol/parc_JSONParser.c @@ -0,0 +1,181 @@ +/* + * 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 <LongBow/runtime.h> + +#include <ctype.h> +#include <string.h> + +#include <parc/algol/parc_JSONParser.h> + +#include <parc/algol/parc_BufferComposer.h> +#include <parc/algol/parc_Object.h> + +struct parc_buffer_parser { + char *ignore; + PARCBuffer *buffer; +}; + +static PARCBuffer * +_getBuffer(const PARCJSONParser *parser) +{ + return parser->buffer; +} + +static void +_destroyPARCBufferParser(PARCJSONParser **instancePtr) +{ + PARCJSONParser *parser = *instancePtr; + parcBuffer_Release(&parser->buffer); +} + +parcObject_ExtendPARCObject(PARCJSONParser, _destroyPARCBufferParser, NULL, NULL, NULL, NULL, NULL, NULL); + +PARCJSONParser * +parcJSONParser_Create(PARCBuffer *buffer) +{ + PARCJSONParser *result = parcObject_CreateInstance(PARCJSONParser); + result->ignore = " \t\n"; + result->buffer = parcBuffer_Acquire(buffer); + return result; +} + +void +parcJSONParser_AssertValid(const PARCJSONParser *parser) +{ + assertNotNull(parser, "PARCJSONParser cannot be NULL"); + parcBuffer_OptionalAssertValid(parser->buffer); +} + +parcObject_ImplementAcquire(parcJSONParser, PARCJSONParser); + +parcObject_ImplementRelease(parcJSONParser, PARCJSONParser); + +void +parcJSONParser_SkipIgnored(PARCJSONParser *parser) +{ + parcJSONParser_OptionalAssertValid(parser); + + parcBuffer_SkipOver(parser->buffer, strlen(parser->ignore), (uint8_t *) parser->ignore); +} + +char +parcJSONParser_NextChar(PARCJSONParser *parser) +{ + parcJSONParser_SkipIgnored(parser); + return (char) parcBuffer_GetUint8(parser->buffer); +} + +bool +parcJSONParser_Next(PARCJSONParser *parser, char *value) +{ + bool result = false; + parcJSONParser_SkipIgnored(parser); + if (parcJSONParser_Remaining(parser) > 0) { + *value = (char) parcBuffer_GetUint8(parser->buffer); + result = true; + } + return result; +} + +char +parcJSONParser_PeekNextChar(PARCJSONParser *parser) +{ + parcJSONParser_SkipIgnored(parser); + return (char) parcBuffer_PeekByte(parser->buffer); +} + +void +parcJSONParser_Advance(PARCJSONParser *parser, long bytes) +{ + parcJSONParser_OptionalAssertValid(parser); + + parcBuffer_SetPosition(parser->buffer, parcBuffer_Position(parser->buffer) + bytes); +} + +size_t +parcJSONParser_Remaining(const PARCJSONParser *parser) +{ + parcJSONParser_OptionalAssertValid(parser); + + return parcBuffer_Remaining(parser->buffer); +} + +bool +parcJSONParser_RequireString(PARCJSONParser *parser, const char *string) +{ + PARCBuffer *buffer = _getBuffer(parser); + + for (const char *requiredCharacter = string; *requiredCharacter != 0; requiredCharacter++) { + uint8_t actualCharacter = parcBuffer_GetUint8(buffer); + if (actualCharacter != *requiredCharacter) { + return false; + } + } + return true; +} + +PARCBuffer * +parcJSONParser_ParseString(PARCJSONParser *parser) +{ + PARCBuffer *result = NULL; + + PARCBuffer *buffer = _getBuffer(parser); + if (parcBuffer_GetUint8(buffer) == '"') { // skip the initial '"' character starting the string. + PARCBufferComposer *composer = parcBufferComposer_Create(); + + while (parcBuffer_Remaining(buffer)) { + uint8_t c = parcBuffer_GetUint8(buffer); + if (c == '"') { + // This is the only successful way to exit this while loop. + result = parcBufferComposer_ProduceBuffer(composer); + break; + } else if (c == '\\') { + c = parcBuffer_GetUint8(buffer); + if (c == '"') { + // this special character passes directly into the composed string. + } else if (c == '\\') { + // this special character passes directly into the composed string. + } else if (c == '/') { + // this special character passes directly into the composed string. + } else if (c == 'b') { + c = '\b'; + } else if (c == 'f') { + c = '\f'; + } else if (c == 'n') { + c = '\n'; + } else if (c == 'r') { + c = '\r'; + } else if (c == 't') { + c = '\t'; + } else if (c == 'u') { + // Not supporting unicode at this point. + trapNotImplemented("Unicode is not supported."); + } + } else if (iscntrl(c)) { + // !! Syntax Error. + break; + } + parcBufferComposer_PutChar(composer, c); + } + + parcBufferComposer_Release(&composer); + } + return result; +} diff --git a/libparc/parc/algol/parc_JSONParser.h b/libparc/parc/algol/parc_JSONParser.h new file mode 100755 index 00000000..18ee9d1a --- /dev/null +++ b/libparc/parc/algol/parc_JSONParser.h @@ -0,0 +1,309 @@ +/* + * 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. + */ + +/** + * @file parc_JSONParser.h + * @brief A JSON parser + * @ingroup inputoutput + * + */ +#ifndef PARC_Library_parc_JSONParser_h +#define PARC_Library_parc_JSONParser_h + +struct parc_buffer_parser; +typedef struct parc_buffer_parser PARCJSONParser; + +#include <parc/algol/parc_Buffer.h> + +/** + * @def parcJSONValue_OptionalAssertValid + * Optional validation of the given instance. + * + * Define `PARCLibrary_DISABLE_VALIDATION` to nullify validation. + */ +#ifdef PARCLibrary_DISABLE_VALIDATION +# define parcJSONParser_OptionalAssertValid(_instance_) +#else +# define parcJSONParser_OptionalAssertValid(_instance_) parcJSONParser_AssertValid(_instance_) +#endif + + +/** + * Create a new `PARCJSONParser`. + * + * @param [in] buffer A pointer to a {@link PARCBuffer} containing the data to parse. + * + * @return non-NULL A pointer to a valid `PARCJSONParser`. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_WrapCString(" { \"name\" : 123 }"); + * + * PARCJSONParser *parser = parcJSONParser_Create(buffer); + * + * parcJSONParser_Release(&parser); + * parcBuffer_Release(&buffer); + * } + * @endcode + */ +PARCJSONParser *parcJSONParser_Create(PARCBuffer *buffer); + +/** + * Assert that an instance of `PARCJSONParser` is valid. + * + * If the instance is not valid, terminate via {@link trapIllegalValue()} + * + * Valid means the internal state of the type is consistent with its required current or future behaviour. + * This may include the validation of internal instances of types. + * + * @param [in] parser A pointer to a `PARCJSONParser` instance. + */ +void parcJSONParser_AssertValid(const PARCJSONParser *parser); + +/** + * Increase the number of references to a `PARCJSONParser`. + * + * Note that new `PARCJSONParser` is not created, + * only that the given `PARCJSONParser` reference count is incremented. + * Discard the reference by invoking {@link parcJSONParser_Release}. + * + * @param parser A pointer to the original instance. + * @return The value of the input parameter @p parser. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_WrapCString(" { \"name\" : 123 }"); + * + * PARCJSONParser *parser = parcJSONParser_Create(buffer); + * PARCJSONParser *x2 = parcJSONParser_Acquire(parser); + * + * parcJSONParser_Release(&parser); + * parcJSONParser_Release(&x2); + * } + * @endcode + * + * @see parcJSONParser_Release + */ +PARCJSONParser *parcJSONParser_Acquire(const PARCJSONParser *parser); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * The contents of the dealloced memory used for the PARC object are undefined. + * Do not reference the object after the last release. + * + * @param [in,out] parserPtr A pointer to a pointer to the instance of `PARCJSONParser` to release. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_WrapCString(" { \"name\" : 123 }"); + * + * PARCJSONParser *parser = parcJSONParser_Create(buffer); + * + * parcJSONParser_Release(&parser); + * } + * @endcode + */ +void parcJSONParser_Release(PARCJSONParser **parserPtr); + +/** + * Advance the parser, skipping any ignored characters. + * + * Ignored characters are space, tab and new-line. + * + * @param [in] parser A pointer to a `PARCJSONParser` instance. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_WrapCString(" { \"name\" : 123 }"); + * + * PARCJSONParser *parser = parcJSONParser_Create(buffer); + * parcJSONParser_SkipIgnored(parser); + * + * parcJSONParser_Release(&parser); + * } + * @endcode + */ +void parcJSONParser_SkipIgnored(PARCJSONParser *parser); + +/** + * Get the next character from the parser. + * + * @param [in] parser A pointer to a `PARCJSONParser` instance. + * + * @return The next character + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_WrapCString(" { \"name\" : 123 }"); + * + * PARCJSONParser *parser = parcJSONParser_Create(buffer); + * char c = parcJSONParser_NextChar(parser); + * + * parcJSONParser_Release(&parser); + * } + * @endcode + */ +char parcJSONParser_NextChar(PARCJSONParser *parser); + +/** + * Get the next character from the parser, returning true or false if successful. + * + * @param [in] parser A pointer to a `PARCJSONParser` instance. + * @param [out] value A pointer to a `char` to contain the value if successful. + * + * @return true If successful + * @return false If unsuccessful + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_WrapCString(" { \"name\" : 123 }"); + * + * PARCJSONParser *parser = parcJSONParser_Create(buffer); + * bool success = parcJSONParser_Next(parser, &c); + * + * parcJSONParser_Release(&parser); + * } + * @endcode + */ +bool parcJSONParser_Next(PARCJSONParser *parser, char *value); + +/** + * Get the next character that the parser will process, but do not process it nor advance the parser. + * + * @param [in] parser A pointer to a `PARCJSONParser` instance. + * + * @return The next character that the parser will process. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_WrapCString(" { \"name\" : 123 }"); + * + * PARCJSONParser *parser = parcJSONParser_Create(buffer); + * char c = parcJSONParser_PeekNextChar(parser); + * + * parcJSONParser_Release(&parser); + * } + * @endcode + */ +char parcJSONParser_PeekNextChar(PARCJSONParser *parser); + +/** + * Advance the position of the parser forward or backward by the given number of bytes. + * + * To advance forward, bytes is a positive value. + * To advance backwards, bytes is a negative value. + * + * @param [in] parser A pointer to a valid `PARCJSONParser`. + * @param [in] bytes The number of bytes to move forward or backward. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_WrapCString("abcdef"); + * + * PARCJSONParser *parser = parcJSONParser_Create(buffer); + * parcJSONParser_Advance(parser, 2); + * + * parcJSONParser_Release(&parser); + * } + * @endcode + */ +void parcJSONParser_Advance(PARCJSONParser *parser, long bytes); + +/** + * Get the number of characters remaining to be parsed. + * + * @param [in] parser A pointer to a valid `PARCJSONParser` instance + * + * @return The number of characters remaining to be parsed. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_WrapCString("true); + * + * PARCJSONParser *parser = parcJSONParser_Create(buffer); + * size_t remaining = parcJSONParser_Remaining(parser); + * + * parcJSONParser_Release(&parser); + * } + * @endcode + */ +size_t parcJSONParser_Remaining(const PARCJSONParser *parser); + +/** + * Require the fixed string to appear in the current position of the parser. + * + * @param [in] parser A pointer to a `PARCJSONParser` instance. + * @param [in] string A pointer to a null-terminated C-string that must appear at the current position of the parser. + * + * @return true If the string appears. + * @return false If the string does not appear + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_WrapCString("true"); + * + * PARCJSONParser *parser = parcJSONParser_Create(buffer); + * bool result = parcJSONParser_RequireString(parser, "true"); + * + * parcJSONParser_Release(&parser); + * parcBuffer_Release(&buffer); + * } + * @endcode + */ +bool parcJSONParser_RequireString(PARCJSONParser *parser, const char *string); + +/** + * Parse a JSON string returning a {@link PARCBuffer} containing the parsed string. + * + * A JSON string begins and ends with a non-escaped double-quote character. + * + * @param [in] parser A pointer to a `PARCJSONParser` instance. + * + * @return non-NULL A pointer to a valid `PARCBuffer`. + * @return NULL An error occurred. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_WrapCString("\"name\" : 123"); + * + * PARCJSONParser *parser = parcJSONParser_Create(buffer); + * PARCBuffer *theName = parcJSONParser_ParseString(parser); + * + * parcJSONParser_Release(&parser); + * } + * @endcode + */ +PARCBuffer *parcJSONParser_ParseString(PARCJSONParser *parser); + +#endif diff --git a/libparc/parc/algol/parc_JSONValue.c b/libparc/parc/algol/parc_JSONValue.c new file mode 100755 index 00000000..3a3ba9cd --- /dev/null +++ b/libparc/parc/algol/parc_JSONValue.c @@ -0,0 +1,1018 @@ +/* + * 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 <LongBow/runtime.h> + +#include <stdio.h> +#include <ctype.h> +#include <math.h> +#include <string.h> +#include <inttypes.h> + +#include <parc/algol/parc_JSONValue.h> +#include <parc/algol/parc_JSON.h> + +#include <parc/algol/parc_DisplayIndented.h> +#include <parc/algol/parc_Object.h> + +typedef enum { + PARCJSONValueType_Boolean, + PARCJSONValueType_String, + PARCJSONValueType_Number, + PARCJSONValueType_Array, + PARCJSONValueType_JSON, + PARCJSONValueType_Null +} _PARCJSONValueType; + +struct parc_json_value { + _PARCJSONValueType type; + + union { + bool boolean; + PARCBuffer *string; + int64_t intValue; + PARCList *_array; + PARCJSONArray *array; + PARCJSON *object; + struct { + bool internalDoubleRepresentation; + long double internalDoubleValue; + + int sign; + int64_t whole; + int64_t fraction; + int64_t fractionLog10; + int64_t exponent; + } number; + } value; +}; + + + +static void +_parcJSONValueDestroy(PARCJSONValue **valuePtr) +{ + if (valuePtr != NULL) { + PARCJSONValue *value = *valuePtr; + if (value->type == PARCJSONValueType_Array) { + parcJSONArray_Release(&value->value.array); + } else if (value->type == PARCJSONValueType_JSON) { + parcJSON_Release(&value->value.object); + } else if (value->type == PARCJSONValueType_String) { + parcBuffer_Release(&value->value.string); + } + } +} + +parcObject_ExtendPARCObject(PARCJSONValue, _parcJSONValueDestroy, NULL, NULL, parcJSONValue_Equals, NULL, NULL, NULL); + +static PARCJSONValue * +_createValue(_PARCJSONValueType type) +{ + PARCJSONValue *result = parcObject_CreateAndClearInstance(PARCJSONValue); + + if (result != NULL) { + result->type = type; + } + return result; +} + +/** + * Return true if the parser is currently positioned at the valid beginning of a number. + * If true, then return the sign (-1, +1) in the integer pointed to by @p sign. + * If false, then return false ensuring that the parser is repositioned to where it started. + */ +static bool +_parseSign(PARCJSONParser *parser, int *sign) +{ + if (parcJSONParser_Remaining(parser) > 0) { + uint8_t c = parcJSONParser_NextChar(parser); + if (c == '-') { + *sign = -1; + return true; + } + if (!isdigit(c)) { + return false; + } + parcJSONParser_Advance(parser, -1); + } + *sign = 1; + return true; +} + +static PARCJSONValue * +_parcJSONValue_FalseParser(PARCJSONParser *parser) +{ + PARCJSONValue *result = NULL; + + if (parcJSONParser_RequireString(parser, "false")) { + result = parcJSONValue_CreateFromBoolean(false); + } + return result; +} + +static PARCJSONValue * +_parcJSONValue_NullParser(PARCJSONParser *parser) +{ + PARCJSONValue *result = NULL; + + if (parcJSONParser_RequireString(parser, "null")) { + result = parcJSONValue_CreateFromNULL(); + } + return result; +} + +static PARCJSONValue * +_parcJSONValue_TrueParser(PARCJSONParser *parser) +{ + PARCJSONValue *result = NULL; + + if (parcJSONParser_RequireString(parser, "true")) { + result = parcJSONValue_CreateFromBoolean(true); + } + return result; +} + +static PARCJSONValue * +_parcJSONValue_StringParser(PARCJSONParser *parser) +{ + PARCJSONValue *result = NULL; + PARCBuffer *string = parcJSONParser_ParseString(parser); + + if (string != NULL) { + result = parcJSONValue_CreateFromString(string); + parcBuffer_Release(&string); + } + + return result; +} + +static int +_digittoint(char digit) +{ + return digit - '0'; +} + +/* + * Parse the whole number portion of a number. + * + * 0 + * [1-9][0-9]* + */ +static bool +_parseWholeNumber(PARCJSONParser *parser, int64_t *value) +{ + bool result = false; + int sign = 1; + + char nextCharacter; + + if (parcJSONParser_Next(parser, &nextCharacter)) { + if (nextCharacter == '0') { + *value = 0; + result = true; + } else if (isdigit(nextCharacter)) { + *value = _digittoint(nextCharacter); + while (parcJSONParser_Next(parser, &nextCharacter)) { + if (!isdigit(nextCharacter)) { + parcJSONParser_Advance(parser, -1); + break; + } + *value = *value * 10 + _digittoint(nextCharacter); + } + *value = *value * sign; + result = true; + } + } + + return result; +} + +static bool +_parseFractionNumber(PARCJSONParser *parser, int64_t *value, int *log10) +{ + bool result = false; + + if (parcJSONParser_Remaining(parser) > 0) { + *value = 0; + *log10 = 0; + char nextCharacter; + while (parcJSONParser_Next(parser, &nextCharacter)) { + if (!isdigit(nextCharacter)) { + parcJSONParser_Advance(parser, -1); + break; + } + *value = *value * 10 + _digittoint(nextCharacter); + *log10 = *log10 + 1; + } + + result = true; + } + + return result; +} + +/** + * Parse an optional fractional part of a number. + * + * If the parser is positioned at a '.' character, then parse a fraction comprised of numbers. + * Otherwise, if the parser is positioned at a 'e' ',' ']' or '}' then there is no fraction, but not an error. + * If the parser is positioned at any other character, then it is a syntax error. + * + * @param [in] parser A pointer to a PARCJSONParser instance. + * @param [out] value A pointer to an integer accumulating the fraction as a whole number. + * @param [out] log10 A pointer to an integer accumulating the base 10 logarithm of the fraction (as a positive integer). + * + * @return true If there was no syntax error. + * @return false If there was a syntax error. + */ +static bool +_parseOptionalFraction(PARCJSONParser *parser, int64_t *value, int *log10) +{ + bool result = true; + + // The parser is either looking at an '.' which signals the start of a fractional part, + // or a 'e' ',' ']' or '}' which signals a missing fractional part. + // Any other character would be the beginning of a syntax error. + + char nextCharacter; + + if (parcJSONParser_Next(parser, &nextCharacter)) { + if (nextCharacter == '.') { + if (_parseFractionNumber(parser, value, log10) == false) { + result = false; + } + } else if (nextCharacter == 'e' || nextCharacter == ',' || nextCharacter == ']' || nextCharacter == '}') { + parcJSONParser_Advance(parser, -1); + result = true; + } else { + parcJSONParser_Advance(parser, -1); + result = false; + } + } + + return result; +} + +/** + * Parse and compute the base 10 value of a a sequence of digits from 0 to 9, inclusive. + * + * @param [in] parser A pointer to a PARCJSONParser instance. + * @param [out] value A pointer to a value that will receive the base 10 value. + * + * @return true If there were parsable digits. + */ +static bool +_parseDigits09(PARCJSONParser *parser, int64_t *value) +{ + bool result = false; + + *value = 0; + char nextDigit; + while (parcJSONParser_Next(parser, &nextDigit)) { + *value = *value * 10 + _digittoint(nextDigit); + result = true; + } + + return result; +} + +static bool +_parseExponentNumber(PARCJSONParser *parser, int64_t *value) +{ + bool result = false; + int sign = 1; + + char nextCharacter; + if (parcJSONParser_Next(parser, &nextCharacter)) { + if (nextCharacter == '-') { + sign = -1; + if (_parseDigits09(parser, value)) { + result = true; + } + *value = *value * sign; + } else if (nextCharacter == '+') { + sign = 1; + if (_parseDigits09(parser, value)) { + result = true; + } + *value = *value * sign; + } else if (isdigit(nextCharacter)) { + parcJSONParser_Advance(parser, -1); + if (_parseDigits09(parser, value)) { + result = true; + } + *value = *value * sign; + } else { + result = false; + } + } + + return result; +} + +static bool +_parseOptionalExponent(PARCJSONParser *parser, int64_t *value) +{ + // The parser is either looking at a 'e' or 'E' ',' ']' or '}' + bool result = true; + + char nextCharacter; + if (parcJSONParser_Next(parser, &nextCharacter)) { + if (nextCharacter == 'e' || nextCharacter == 'E') { + if (_parseExponentNumber(parser, value) == false) { + result = false; + } + } else if (nextCharacter == ',' || nextCharacter == ']' || nextCharacter == '}') { + parcJSONParser_Advance(parser, -1); + result = true; + } else { + parcJSONParser_Advance(parser, -1); + result = false; + } + } + + return result; +} + +static __attribute__ ((noinline)) PARCJSONValue * +_parcJSONValue_CreateNumber(int sign, int64_t whole, int64_t fraction, int64_t fractionLog10, int64_t exponent) +{ + PARCJSONValue *result = _createValue(PARCJSONValueType_Number); + if (result != NULL) { + result->value.number.sign = sign; + result->value.number.whole = whole; + result->value.number.fraction = fraction; + result->value.number.fractionLog10 = fractionLog10; + result->value.number.exponent = exponent; + } + return result; +} + +static PARCJSONValue * +_parcJSONValue_NumberParser(PARCJSONParser *parser) +{ + PARCJSONValue *result = NULL; + int sign = 1; + int64_t whole = 0; + int64_t fraction = 0; + int64_t exponent = 0; + int fractionLog10 = 0; + + if (_parseSign(parser, &sign)) { + if (_parseWholeNumber(parser, &whole)) { + if (_parseOptionalFraction(parser, &fraction, &fractionLog10)) { + if (_parseOptionalExponent(parser, &exponent)) { + result = _parcJSONValue_CreateNumber(sign, whole, fraction, fractionLog10, exponent); + } + } + } + } + + return result; +} + +static PARCJSONValue * +_parcJSONValue_ArrayParser(PARCJSONParser *parser) +{ + PARCJSONValue *result = NULL; + + if (parcJSONParser_NextChar(parser) == '[') { + PARCJSONArray *array = parcJSONArray_Create(); + + while (parcJSONParser_Remaining(parser)) { + char peek = parcJSONParser_PeekNextChar(parser); + if (peek == ',') { + parcJSONParser_NextChar(parser); + } else if (peek == ']') { + parcJSONParser_NextChar(parser); // absorb the ']' character + result = parcJSONValue_CreateFromJSONArray(array); + parcJSONArray_Release(&array); + break; + } else { + PARCJSONValue *value = NULL; + + if (peek == 'n') { + value = _parcJSONValue_NullParser(parser); + } else if (peek == 't') { + value = _parcJSONValue_TrueParser(parser); + } else if (peek == 'f') { + value = _parcJSONValue_FalseParser(parser); + } else if (peek == '"') { + value = _parcJSONValue_StringParser(parser); + } else if (peek == '{') { + value = parcJSONValue_ObjectParser(parser); + } else if (peek == '[') { + value = _parcJSONValue_ArrayParser(parser); + } else { + value = _parcJSONValue_NumberParser(parser); + } + + if (value != NULL) { + parcJSONArray_AddValue(array, value); + parcJSONValue_Release(&value); + } else { + parcJSONArray_Release(&array); + break; + } + } + } + } + + return result; +} + +static void +_displayBoolean(const PARCJSONValue *value, int indentation) +{ + parcDisplayIndented_PrintLine(indentation, ".value=%s", value->value.boolean == true ? "true" : "false"); +} + +static void +_displayNumber(const PARCJSONValue *value, int indentation) +{ + if (value->value.number.internalDoubleRepresentation) { + parcDisplayIndented_PrintLine(indentation, ".value=%Lf", value->value.number.internalDoubleValue); + } else { + parcDisplayIndented_PrintLine(indentation, + ".value.number={ sign=%d whole=%lld fractionLog10=%d fraction=%lld exponent=%lld", + value->value.number.sign, + value->value.number.whole, + (int) value->value.number.fractionLog10, + value->value.number.fraction, + value->value.number.exponent); + } +} + +void +parcJSONValue_AssertValid(const PARCJSONValue *value) +{ + assertNotNull(value, "PARCJSONValue cannot be NULL."); +} + +bool +parcJSONValue_IsValid(const PARCJSONValue *value) +{ + bool result = true; + + if (value == NULL) { + result = false; + } + + return result; +} + +bool +parcJSONValue_IsNull(const PARCJSONValue *value) +{ + parcJSONValue_OptionalAssertValid(value); + + return (value->type == PARCJSONValueType_Null); +} + +bool +parcJSONValue_IsBoolean(const PARCJSONValue *value) +{ + parcJSONValue_OptionalAssertValid(value); + + return (value->type == PARCJSONValueType_Boolean); +} + +bool +parcJSONValue_IsNumber(const PARCJSONValue *value) +{ + parcJSONValue_OptionalAssertValid(value); + + return (value->type == PARCJSONValueType_Number); +} + +bool +parcJSONValue_IsJSON(const PARCJSONValue *value) +{ + parcJSONValue_OptionalAssertValid(value); + + return (value->type == PARCJSONValueType_JSON); +} + +bool +parcJSONValue_IsString(const PARCJSONValue *value) +{ + parcJSONValue_OptionalAssertValid(value); + + return (value->type == PARCJSONValueType_String); +} + +bool +parcJSONValue_IsArray(const PARCJSONValue *value) +{ + parcJSONValue_OptionalAssertValid(value); + + return (value->type == PARCJSONValueType_Array); +} + +PARCJSONValue * +parcJSONValue_CreateFromNULL(void) +{ + // Strictly speaking, this could just be a singleton, rather than allocated every time. + + PARCJSONValue *result = _createValue(PARCJSONValueType_Null); + + return result; +} + +PARCJSONValue * +parcJSONValue_CreateFromBoolean(bool value) +{ + PARCJSONValue *result = _createValue(PARCJSONValueType_Boolean); + if (result != NULL) { + result->value.boolean = value; + } + return result; +} + +PARCJSONValue * +parcJSONValue_CreateFromFloat(long double value) +{ + PARCJSONValue *result = _parcJSONValue_CreateNumber(0, 0, 0, 0, 0); + result->value.number.internalDoubleRepresentation = true; + result->value.number.internalDoubleValue = value; + return result; +} + +PARCJSONValue * +parcJSONValue_CreateFromInteger(int64_t value) +{ + PARCJSONValue *result = _parcJSONValue_CreateNumber(1, value, 0, 0, 0); + return result; +} + +PARCJSONValue * +parcJSONValue_CreateFromString(PARCBuffer *value) +{ + parcBuffer_OptionalAssertValid(value); + + PARCJSONValue *result = _createValue(PARCJSONValueType_String); + if (result != NULL) { + result->value.string = parcBuffer_Acquire(value); + } + return result; +} + +PARCJSONValue * +parcJSONValue_CreateFromCString(const char *value) +{ + assertNotNull(value, "String cannot be NULL."); + + PARCJSONValue *result = _createValue(PARCJSONValueType_String); + if (result != NULL) { + result->value.string = parcBuffer_AllocateCString(value); + } + return result; +} + +PARCJSONValue * +parcJSONValue_CreateFromJSONArray(PARCJSONArray *value) +{ + PARCJSONValue *result = _createValue(PARCJSONValueType_Array); + if (result != NULL) { + result->value.array = parcJSONArray_Acquire(value); + } + return result; +} + +PARCJSONValue * +parcJSONValue_CreateFromJSON(PARCJSON *value) +{ + PARCJSONValue *result = _createValue(PARCJSONValueType_JSON); + if (result != NULL) { + result->value.object = parcJSON_Acquire(value); + } + return result; +} + +PARCJSONValue * +parcJSONValue_CreateFromTimeval(const struct timeval *timeval) +{ + PARCJSON *jsonTimeval = parcJSON_Create(); + parcJSON_AddInteger(jsonTimeval, "seconds", timeval->tv_sec); + parcJSON_AddInteger(jsonTimeval, "micros", timeval->tv_usec); + + PARCJSONValue *result = _createValue(PARCJSONValueType_JSON); + if (result != NULL) { + result->value.object = jsonTimeval; + } + + return result; +} + +PARCJSONValue * +parcJSONValue_CreateFromTimespec(const struct timespec *timespec) +{ + PARCJSON *jsonTimespec = parcJSON_Create(); + parcJSON_AddInteger(jsonTimespec, "seconds", timespec->tv_sec); + parcJSON_AddInteger(jsonTimespec, "nanos", timespec->tv_nsec); + + PARCJSONValue *result = _createValue(PARCJSONValueType_JSON); + if (result != NULL) { + result->value.object = jsonTimespec; + } + + return result; +} + +void +parcJSONValue_Display(const PARCJSONValue *value, int indentation) +{ + parcDisplayIndented_PrintLine(indentation, "PARCJSONValue@%p {", value); + if (value != NULL) { + parcDisplayIndented_PrintLine(indentation + 1, ".type=%d", value->type); + + switch (value->type) { + case PARCJSONValueType_Boolean: + _displayBoolean(value, indentation + 1); + break; + case PARCJSONValueType_String: + parcBuffer_Display(value->value.string, indentation + 1); + break; + case PARCJSONValueType_Number: + _displayNumber(value, indentation + 1); + break; + case PARCJSONValueType_Array: + parcJSONArray_Display(value->value.array, indentation + 1); + break; + case PARCJSONValueType_JSON: + parcJSON_Display(value->value.object, indentation + 1); + break; + case PARCJSONValueType_Null: + parcDisplayIndented_PrintLine(indentation + 1, ".value=null"); + break; + default: + trapIllegalValue(value->type, "Unknown PARCJSONValue type %d", value->type); + } + } + parcDisplayIndented_PrintLine(indentation, "}"); +} + +parcObject_ImplementAcquire(parcJSONValue, PARCJSONValue); + +parcObject_ImplementRelease(parcJSONValue, PARCJSONValue); + +static bool +_equalsNumber(const PARCJSONValue *valueA, const PARCJSONValue *valueB) +{ + bool result = false; + + if (valueA->value.number.internalDoubleRepresentation) { + if (valueB->value.number.internalDoubleRepresentation) { + if (valueA->value.number.internalDoubleValue == valueB->value.number.internalDoubleValue) { + result = true; + } + } + } else { + if (valueA->value.number.sign == valueB->value.number.sign) { + if (valueA->value.number.whole == valueB->value.number.whole) { + if (valueA->value.number.fraction == valueB->value.number.fraction) { + if (valueA->value.number.exponent == valueB->value.number.exponent) { + result = true; + } + } + } + } + } + + return result; +} + +bool +parcJSONValue_Equals(const PARCJSONValue *objA, const PARCJSONValue *objB) +{ + bool result = false; + + if (objA == NULL || objB == NULL) { + result = (objA == objB); + } else { + if (objA->type == objB->type) { + switch (objA->type) { + case PARCJSONValueType_Boolean: + result = objA->value.boolean == objB->value.boolean; + break; + case PARCJSONValueType_String: + result = parcBuffer_Equals(objA->value.string, objB->value.string); + break; + case PARCJSONValueType_Number: + result = _equalsNumber(objA, objB); + break; + case PARCJSONValueType_Array: + result = parcJSONArray_Equals(objA->value.array, objB->value.array); + break; + case PARCJSONValueType_JSON: + result = parcJSON_Equals(objA->value.object, objB->value.object); + break; + case PARCJSONValueType_Null: + result = true; + break; + } + } + } + + return result; +} + +PARCJSONArray * +parcJSONValue_GetArray(const PARCJSONValue *value) +{ + parcJSONValue_OptionalAssertValid(value); + + trapUnexpectedStateIf(!parcJSONValue_IsArray(value), "Expected type to be array, actual type %d", value->type); + + return value->value.array; +} + +bool +parcJSONValue_GetBoolean(const PARCJSONValue *value) +{ + parcJSONValue_OptionalAssertValid(value); + + trapUnexpectedStateIf(!parcJSONValue_IsBoolean(value), "Expected type to be boolean, actual type %d", value->type); + + return value->value.boolean; +} + +static long double +_parcJSONValue_GetNumber(const PARCJSONValue *value) +{ + long double fraction = value->value.number.fraction / powl(10.0, value->value.number.fractionLog10); + long double number = (long double) value->value.number.sign * ((long double) value->value.number.whole + fraction); + + long double result = number * powl(10.0, (long double) value->value.number.exponent); + + return result; +} + +long double +parcJSONValue_GetFloat(const PARCJSONValue *value) +{ + parcJSONValue_OptionalAssertValid(value); + + long double result = 0; + + if (value->value.number.internalDoubleRepresentation) { + result = value->value.number.internalDoubleValue; + } else { + result = _parcJSONValue_GetNumber(value); + } + + return result; +} + +int64_t +parcJSONValue_GetInteger(const PARCJSONValue *value) +{ + int64_t result = llrint(_parcJSONValue_GetNumber(value)); + return result; +} + +PARCBuffer * +parcJSONValue_GetString(const PARCJSONValue *value) +{ + parcJSONValue_OptionalAssertValid(value); + + trapUnexpectedStateIf(!parcJSONValue_IsString(value), "Expected type to be string, actual type %d", value->type); + + return value->value.string; +} + +PARCJSON * +parcJSONValue_GetJSON(const PARCJSONValue *value) +{ + parcJSONValue_OptionalAssertValid(value); + + trapUnexpectedStateIf(!parcJSONValue_IsJSON(value), "Expected type to be string, actual type %d", value->type); + + return value->value.object; +} + +struct timeval * +parcJSONValue_GetTimeval(const PARCJSONValue *jsonTimeval, struct timeval *timeval) +{ + assertNotNull(jsonTimeval, "Parameter jsonTimeval must be a non-null PARCJSON pointer."); + + PARCJSON *json = parcJSONValue_GetJSON(jsonTimeval); + PARCJSONValue *value = parcJSON_GetValueByName(json, "seconds"); + timeval->tv_sec = parcJSONValue_GetInteger(value); + value = parcJSON_GetValueByName(json, "micros"); + timeval->tv_usec = (int) parcJSONValue_GetInteger(value); + + return timeval; +} + +struct timespec * +parcJSONValue_GetTimespec(const PARCJSONValue *jsonTimespec, struct timespec *timespec) +{ + assertNotNull(jsonTimespec, "Parameter jsonTimeval must be a non-null PARCJSON pointer."); + + PARCJSON *json = parcJSONValue_GetJSON(jsonTimespec); + PARCJSONValue *value = parcJSON_GetValueByName(json, "seconds"); + timespec->tv_sec = parcJSONValue_GetInteger(value); + value = parcJSON_GetValueByName(json, "nanos"); + timespec->tv_nsec = (int) parcJSONValue_GetInteger(value); + + return timespec; +} + +static PARCBufferComposer * +_buildStringNumber(const PARCJSONValue *value, PARCBufferComposer *string) +{ + if (value->value.number.internalDoubleRepresentation) { + parcBufferComposer_Format(string, "%Lf", value->value.number.internalDoubleValue); + } else { + parcBufferComposer_Format(string, "%s%" PRId64, + value->value.number.sign == -1 ? "-" : "", + value->value.number.whole); + if (value->value.number.fraction > 0) { + parcBufferComposer_Format(string, ".%0*" PRId64, + (int) value->value.number.fractionLog10, + value->value.number.fraction); + } + + if (value->value.number.exponent != 0) { + parcBufferComposer_Format(string, "e%" PRId64, + value->value.number.exponent); + } + } + return string; +} + +static PARCBufferComposer * +_buildStringString(const PARCJSONValue *value, PARCBufferComposer *composer, bool compact) +{ + parcBufferComposer_PutChar(composer, '"'); + + while (parcBuffer_Remaining(value->value.string)) { + uint8_t c = parcBuffer_GetUint8(value->value.string); + if (c == '"') { + parcBufferComposer_PutString(composer, "\\\""); + } else if (c == '\b') { + parcBufferComposer_PutString(composer, "\\b"); + } else if (c == '\f') { + parcBufferComposer_PutString(composer, "\\f"); + } else if (c == '\n') { + parcBufferComposer_PutString(composer, "\\n"); + } else if (c == '\r') { + parcBufferComposer_PutString(composer, "\\r"); + } else if (c == '\t') { + parcBufferComposer_PutString(composer, "\\t"); + } else if ((c == '/') && !compact) { + parcBufferComposer_PutString(composer, "\\/"); + } else if (c == '\\') { + parcBufferComposer_PutString(composer, "\\\\"); + } else { + parcBufferComposer_PutChar(composer, c); + } + } + + parcBuffer_Rewind(value->value.string); + parcBufferComposer_PutChar(composer, '"'); + + return composer; +} + +PARCBufferComposer * +parcJSONValue_BuildString(const PARCJSONValue *value, PARCBufferComposer *composer, bool compact) +{ + parcJSONValue_OptionalAssertValid(value); + + if (value->type == PARCJSONValueType_Boolean) { + parcBufferComposer_PutString(composer, value->value.boolean ? "true" : "false"); + } else if (value->type == PARCJSONValueType_String) { + _buildStringString(value, composer, compact); + } else if (value->type == PARCJSONValueType_Number) { + _buildStringNumber(value, composer); + } else if (value->type == PARCJSONValueType_Array) { + parcJSONArray_BuildString(value->value.array, composer, compact); + } else if (value->type == PARCJSONValueType_JSON) { + parcJSON_BuildString(value->value.object, composer, compact); + } else if (value->type == PARCJSONValueType_Null) { + parcBufferComposer_PutString(composer, "null"); + } else { + trapIllegalValue(value->type, "Unknown value type: %d", value->type); + } + + return composer; +} + +static char * +_parcJSONValue_ToString(const PARCJSONValue *value, bool compact) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + parcJSONValue_BuildString(value, composer, compact); + + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + char *result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + + parcBufferComposer_Release(&composer); + + return result; +} + +char * +parcJSONValue_ToString(const PARCJSONValue *value) +{ + return _parcJSONValue_ToString(value, false); +} + +char * +parcJSONValue_ToCompactString(const PARCJSONValue *value) +{ + return _parcJSONValue_ToString(value, true); +} + +PARCJSONValue * +parcJSONValue_Parser(PARCJSONParser *parser) +{ + char nextCharacter = parcJSONParser_PeekNextChar(parser); + switch (nextCharacter) { + case ',': + break; + + case ']': + return NULL; + + case 'n': + return _parcJSONValue_NullParser(parser); + + case 't': + return _parcJSONValue_TrueParser(parser); + + case 'f': + return _parcJSONValue_FalseParser(parser); + + case '"': + return _parcJSONValue_StringParser(parser); + + case '[': + return _parcJSONValue_ArrayParser(parser); + + case '{': + return parcJSONValue_ObjectParser(parser); + + default: + return _parcJSONValue_NumberParser(parser); + } + + return NULL; +} + +PARCJSONValue * +parcJSONValue_ObjectParser(PARCJSONParser *parser) +{ + PARCJSONValue *result = NULL; + + // absorb the (required) '{' character. + if (parcJSONParser_NextChar(parser) == '{') { + PARCJSON *json = parcJSON_Create(); + + while (parcJSONParser_Remaining(parser)) { + char c = parcJSONParser_PeekNextChar(parser); + if (c == '}') { + // Absorb the '}' and terminate. + parcJSONParser_NextChar(parser); + result = parcJSONValue_CreateFromJSON(json); + break; + } else if (c == ',') { + // absorb the ',' character and continue + parcJSONParser_NextChar(parser); + } else if (c == '"') { + PARCJSONPair *pair = parcJSONPair_Parser(parser); + if (pair == NULL) { + break; + } + parcJSON_AddPair(json, pair); + parcJSONPair_Release(&pair); + } else { + break; + } + } + parcJSON_Release(&json); + } + + return result; +} + diff --git a/libparc/parc/algol/parc_JSONValue.h b/libparc/parc/algol/parc_JSONValue.h new file mode 100755 index 00000000..300a1c0c --- /dev/null +++ b/libparc/parc/algol/parc_JSONValue.h @@ -0,0 +1,938 @@ +/* + * 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. + */ + +/** + * @file parc_JSON.h + * @ingroup inputoutput + * @brief A JSON value. One of a Boolean, String, Number, JSON Array, JSON Object, or Null. + * + */ +#ifndef libparc_parc_JSONValue_h +#define libparc_parc_JSONValue_h + +#include <stdbool.h> +#include <stdint.h> + +struct parc_json_value; +typedef struct parc_json_value PARCJSONValue; + +#include <parc/algol/parc_Object.h> + +#include <parc/algol/parc_JSON.h> +#include <parc/algol/parc_JSONParser.h> +#include <parc/algol/parc_JSONPair.h> +#include <parc/algol/parc_JSONArray.h> + +#include <parc/algol/parc_Buffer.h> +#include <parc/algol/parc_BufferComposer.h> +#include <parc/algol/parc_List.h> + +/** + * @def parcJSONValue_OptionalAssertValid + * Optional validation of the given instance. + * + * Define `PARCLibrary_DISABLE_VALIDATION` to nullify validation. + */ +#ifdef PARCLibrary_DISABLE_VALIDATION +# define parcJSONValue_OptionalAssertValid(_instance_) +#else +# define parcJSONValue_OptionalAssertValid(_instance_) parcJSONValue_AssertValid(_instance_) +#endif + +/** + * Print a human readable representation of the given `PARCJSONValue`. + * + * @param [in] indentation The level of indentation to use to pretty-print the output. + * @param [in] value A pointer to the instance of `PARCJSONValue` to display. + * + * Example: + * @code + * { + * PARCJSONValue *instance = parcJSONValue_CreateFromNull(); + * + * parcJSONValue_Display(instance, 0); + * + * parcJSONValue_Release(&instance); + * } + * @endcode + * + */ +void parcJSONValue_Display(const PARCJSONValue *value, int indentation); + +/** + * Determine if @p value is a JSON Null. + * + * @param [in] value A pointer to a `JSONValue` instance. + * + * @return True if @p value is a JSON Null. + * + * Example: + * @code + * { + * PARCJSONValue *value = parcJSONValue_CreateFromNULL(); + * + * if (parcJSONValue_IsNull(value)) { + * printf("Success!\n"); + * } + * + * parcJSONValue_Release(&value); + * } + * @endcode + * + * @see parcJSONValue_CreateFromNULL + */ +bool parcJSONValue_IsNull(const PARCJSONValue *value); + +/** + * Determine if @p value is a JSON Boolean. + * + * @param [in] value A pointer to a `JSONValue` instance. + * + * @return True if @p value is a JSON Boolean. + * + * Example: + * @code + * { + * PARCJSONValue *value = parcJSONValue_CreateFromBoolean(true); + * + * if (parcJSONValue_IsBoolean(value)) { + * printf("Success!\n"); + * } + * + * parcJSONValue_Release(&value); + * } + * @endcode + * + * @see parcJSONValue_CreateFromNULL + */ +bool parcJSONValue_IsBoolean(const PARCJSONValue *value); + +/** + * Determine if @p value is a JSON Number. + * + * @param [in] value A pointer to a `JSONValue` instance. + * + * @return True if @p value is a JSON Number. + * + * Example: + * @code + * { + * PARCJSONValue *value = parcJSONValue_CreateFromFloat(3.14); + * + * if (parcJSONValue_IsNumber(value)) { + * printf("Success!\n"); + * } + * + * parcJSONValue_Release(&value); + * } + * @endcode + * + * @see parcJSONValue_GetInteger + * @see parcJSONValue_GetFloat + */ +bool parcJSONValue_IsNumber(const PARCJSONValue *value); + +/** + * Determine if the pointer to a `PARCJSONValue` is valid. + * + * @param [in] value A pointer to a `PARCJSONValue`. + * + * @return true The `PARCJSONValue` is valid. + * @return false The `PARCJSONValue` is invalid. + * + * Example: + * @code + * { + * PARCJSON *json = parcJSON_Create()); + * PARCJSONValue *value = parcJSONValue_CreateFromJSON(json); + * parcJSON_Release(&json); + * + * if (parcJSONValue_IsValid(value)) { + * printf("Valid!\n"); + * } else { + * printf("Invalid!\n"); + * } + * + * parcJSONValue_Release(&value); + * } + * @endcode + */ +bool parcJSONValue_IsValid(const PARCJSONValue *value); + +/** + * Assert that an instance of `PARCJSONValue` is valid. + * + * If the instance is not valid, terminate via {@link trapIllegalValue()} + * + * Valid means the internal state of the type is consistent with its required current or future behaviour. + * This may include the validation of internal instances of types. + * + * @param [in] value A pointer to a `PARCJSONValue` instance. + */ +void parcJSONValue_AssertValid(const PARCJSONValue *value); + +/** + * Determine if @p value is a JSON Object. + * + * @param [in] value A pointer to a `JSONValue` instance. + * + * @return True if @p value is a JSON Object. + * + * Example: + * @code + * { + * PARCJSON *json = parcJSON_Create()); + * PARCJSONValue *value = parcJSONValue_CreateFromJSON(json); + * parcJSON_Release(&json); + * + * if (parcJSONValue_IsObject(value)) { + * printf("Success!\n"); + * } + * + * parcJSONValue_Release(&value); + * } + * @endcode + * + * @see parcJSONValue_CreateFromJSON + */ +bool parcJSONValue_IsJSON(const PARCJSONValue *value); + +/** + * Determine if @p value is a JSON String. + * + * @param [in] value A pointer to a `JSONValue` instance. + * + * @return True if @p value is a JSON String. + * + * Example: + * @code + * { + * PARCBuffer *string = parcBuffer_Wrap("Hello", 0, 5); + * PARCJSONValue *value = parcJSONValue_CreateFromString(string); + * parcBuffer_Release(&string); + * + * if (parcJSONValue_IsString(value)) { + * printf("Success!\n"); + * } + * + * parcJSONValue_Release(&value); + * } + * @endcode + * + * @see parcJSONValue_CreateFromString + */ +bool parcJSONValue_IsString(const PARCJSONValue *value); + +/** + * Determine if @p value is a JSON Array. + * + * @param [in] value A pointer to a `JSONValue` instance. + * + * @return True if @p value is a JSON Array. + * + * Example: + * @code + * { + * PARCJSONArray *array = parcJSONArray_Create(); + * PARCJSONValue *value = parcJSONValue_CreateFromJSONArray(array); + * parcJSONArray_Release(&array); + * + * if (parcJSONValue_IsArray(value)) { + * printf("Success!\n"); + * } + * + * parcJSONValue_Release(&value); + * } + * @endcode + * + * @see parcJSONValue_CreateFromJSONArray + */ +bool parcJSONValue_IsArray(const PARCJSONValue *value); + +/** + * Get the value of the given `PARCJSONValue` as a {@link PARCJSONArray} instance. + * + * The value must be a JSON Array. + * + * @param [in] value A pointer to a `JSONValue` instance. + * + * @return A pointer to a `PARCJSONArray` instance. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_WrapCString(" [ ]"); + * + * PARCJSONParser *parser = parcJSONParser_Create(buffer); + * PARCJSONValue *value = parcJSONValue_Parser(parser); + * + * PARCJSONArray *array = parcJSONValue_GetArray(value); + * + * parcJSONValue_Release(&value); + * parcJSONParser_Release(&parser); + * parcBuffer_Release(&buffer); + * } + * @endcode + * + * @see parcJSONValue_IsArray + */ +PARCJSONArray *parcJSONValue_GetArray(const PARCJSONValue *value); + +/** + * Get the given value is a `bool` + * + * The value must be a JSON Array of the type {@link PARCJSONValueType_Boolean} + * + * @param [in] value A pointer to a `JSONValue` instance. + * + * @return A `bool` representation. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_WrapCString(" true"); + * + * PARCJSONParser *parser = parcJSONParser_Create(buffer); + * PARCJSONValue *value = parcJSONValue_Parser(parser); + * + * bool result = parcJSONValue_GetBoolean(value); + * + * parcJSONValue_Release(&value); + * parcJSONParser_Release(&parser); + * parcBuffer_Release(&buffer); + * } + * @endcode + * + * @see {link parcJSONValue_IsBoolean} + */ +bool parcJSONValue_GetBoolean(const PARCJSONValue *value); + +/** + * Get the JSON float value is a `long double` + * + * + * + * @param [in] value A pointer to a `JSONValue` instance. + * + * @return The value as a C double. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_WrapCString(" 3.1415"); + * + * PARCJSONParser *parser = parcJSONParser_Create(buffer); + * PARCJSONValue *value = parcJSONValue_Parser(parser); + * + * long double result = parcJSONValue_GetFloat(value); + * + * parcJSONValue_Release(&value); + * parcJSONParser_Release(&parser); + * parcBuffer_Release(&buffer); + * } + * @endcode + * + * @see parcJSONValue_IsNumber + */ +long double parcJSONValue_GetFloat(const PARCJSONValue *value); + +/** + * Get the given value as an integer. + * + * The value must be a JSON Number value. + * The result must be expressible in a 64-bit integer (`int64_t`). + * Overflow is not detected. + * + * @param [in] value A pointer to a `JSONValue` instance. + * + * @return The value as a C `int64_t`. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_WrapCString(" 31415"); + * + * PARCJSONParser *parser = parcJSONParser_Create(buffer); + * PARCJSONValue *value = parcJSONValue_Parser(parser); + * + * int64_t result = parcJSONValue_GetInteger(value); + * + * parcJSONValue_Release(&value); + * parcJSONParser_Release(&parser); + * parcBuffer_Release(&buffer); + * } + * @endcode + * + * @see parcJSONValue_IsNumber + */ +int64_t parcJSONValue_GetInteger(const PARCJSONValue *value); + +/** + * Get the given value is a null-terminated C string. + * + * The value must be a JSON Array of the type {@link PARCJSONValueType_String} + * + * A new reference to the return value is not created. + * The caller must create a new reference, if it retains a reference to the buffer. + * + * @param [in] value A pointer to a `JSONValue` instance. + * + * @return A pointer to a {@link PARCBuffer} instance. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_WrapCString(" \"a string\""); + * + * PARCJSONParser *parser = parcJSONParser_Create(buffer); + * PARCJSONValue *value = parcJSONValue_Parser(parser); + * + * PARCBuffer *result = parcJSONValue_GetString(value); + * + * parcJSONValue_Release(&value); + * parcJSONParser_Release(&parser); + * parcBuffer_Release(&buffer); + * } + * @endcode + * + * @see parcJSONValue_IsString + */ +PARCBuffer *parcJSONValue_GetString(const PARCJSONValue *value); + +/** + * Get the value of a JSON object. + * + * @param [in] value A pointer to a `JSONValue` instance. + * + * @return The value as a pointer to a {@link PARCJSON} instance. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_WrapCString(" { \"name\" : \"a string\" }"); + * + * PARCJSONParser *parser = parcJSONParser_Create(buffer); + * PARCJSONValue *value = parcJSONValue_Parser(parser); + * + * PARCJSON *result = parcJSONValue_GetJSON(value); + * + * parcJSONValue_Release(&value); + * parcJSONParser_Release(&parser); + * parcBuffer_Release(&buffer); + * } + * @endcode + * + * @see parcJSONValue_IsJSON + */ +PARCJSON *parcJSONValue_GetJSON(const PARCJSONValue *value); + +/** + * Convenience function to fill a timeval struct from a PARCJSONValue. + * + * @param [in] value A pointer to a `JSONValue` instance. + * @param [out] timeval A pre-allocated timeval struct to fill. + * + * @return A pointer to the filled timeval struct. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_WrapCString(" { \"seconds\" : 10, \"micros\" : 0 }"); + * + * PARCJSONParser *parser = parcJSONParser_Create(buffer); + * parcBuffer_Release(&buffer); + * PARCJSONValue *value = parcJSONValue_Parser(parser); + * parcJSONParser_Release(&parser); + * + * struct timeval timeval; + * parcJSONValue_GetTimeval(value, &timeval); + * + * parcJSONValue_Release(&value); + * } + * @endcode + * + */ +struct timeval *parcJSONValue_GetTimeval(const PARCJSONValue *value, struct timeval *timeval); + +/** + * Convenience function to fill a timespec struct from a PARCJSONValue. + * + * @param [in] value A pointer to a `JSONValue` instance. + * @param [out] timespec A pre-allocated timespec struct to fill. + * + * @return A pointer to the filled timespec struct. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_WrapCString(" { \"seconds\" : 10, \"nanos\" : 0 }"); + * + * PARCJSONParser *parser = parcJSONParser_Create(buffer); + * parcBuffer_Release(&buffer); + * PARCJSONValue *value = parcJSONValue_Parser(parser); + * parcJSONParser_Release(&parser); + * + * struct timespec timespec; + * parcJSONValue_GetTimespec(value, ×pec); + * + * parcJSONValue_Release(&value); + * } + * @endcode + * + */ +struct timespec *parcJSONValue_GetTimespec(const PARCJSONValue *value, struct timespec *timespec); + +/** + * Create a NULL JSON value. + * + * @return A pointer to the new `PARCJSONValue`. + * + * Example: + * @code + * { + * PARCJSONValue *value = parcJSONValue_CreateFromNULL(); + * + * parcJSONValue_Release(&value); + * } + * @endcode + * + */ +PARCJSONValue *parcJSONValue_CreateFromNULL(void); + +/** + * Create a boolean value. + * + * @param [in] value Either `true` or `false. + * @return A pointer to the new `PARCJSONValue`. + * + * Example: + * @code + * { + * PARCJSONValue *value = parcJSONValue_CreateFromBoolean(true); + * + * parcJSONValue_Release(&value); + * } + * @endcode + */ +PARCJSONValue *parcJSONValue_CreateFromBoolean(bool value); + +/** + * Create a float value. + * + * The resulting PARCJSONValue is a number (see {@link parcJSONValue_IsNumber}). + * + * @param [in] value A `long double` value. + * @return A pointer to the new `PARCJSONValue`. + * + * Example: + * @code + * { + * PARCJSONValue *value = parcJSONValue_CreateFromFloat(3.14); + * + * parcJSONValue_Release(&value); + * } + * @endcode + */ +PARCJSONValue *parcJSONValue_CreateFromFloat(long double value); + +/** + * Create a JSON integer value. + * + * The resulting PARCJSONValue is a number (see {@link parcJSONValue_IsNumber} ). + * + * @param [in] integer An `int64_t` value. + * @return A pointer to the new `PARCJSONValue`. + * + * Example: + * @code + * { + * PARCJSONValue *value = parcJSONValue_CreateFromInteger(3); + * + * parcJSONValue_Release(&value); + * } + * @endcode + */ +PARCJSONValue *parcJSONValue_CreateFromInteger(int64_t integer); + +/** + * Create a string value from the contents of a {@link PARCBuffer}. + * + * @param [in] string A pointer to a `PARCBuffer` instance. + * @return A pointer to the new `PARCJSONValue`. + * + * Example: + * @code + * { + * PARCBuffer *string = parcBuffer_AllocateCString("hello"): + * PARCJSONValue *value = parcJSONValue_CreateFromString(string); + * + * parcJSONValue_Release(&value); + * parcBuffer_Release(&string); + * } + * @endcode + * @see parcJSONValue_CreateFromCString + */ +PARCJSONValue *parcJSONValue_CreateFromString(PARCBuffer *string); + +/** + * Create a string value from a null-terminated C string. + * + * @param [in] string A pointer to a {@link PARCBuffer} instance. + * @return A pointer to the new `PARCJSONValue`. + * + * Example: + * @code + * { + * PARCJSONValue *value = parcJSONValue_CreateFromCString("hello"); + * + * parcJSONValue_Release(&value); + * } + * @endcode + * @see parcJSONValue_CreateFromString + */ +PARCJSONValue *parcJSONValue_CreateFromCString(const char *string); + +/** + * Create a JSON Array value. + * + * @param [in] array A pointer to a {@link PARCJSONArray} instance. + * @return A pointer to the new `PARCJSONValue`. + * + * Example: + * @code + * { + * PARCJSONArray *array = parcJSONArray_Create(); + * PARCJSONValue *value = parcJSONValue_CreateFromPARCArray(array); + * + * PARCJSONArray_Release(&array); + * parcJSONValue_Release(&value); + * } + * @endcode + */ +PARCJSONValue *parcJSONValue_CreateFromJSONArray(PARCJSONArray *array); + +/** + * Create a JSON Value containing a JSON Object. + * + * A new reference to the given {@link PARCJSON} instance is acquired. + * + * @param [in] json A pointer to a `PARCJSON` instance. + * @return A pointer to the new `PARCJSONValue`. + * + * Example: + * @code + * { + * PARCJSON *json = parcJSON_Create(); + * PARCJSONValue *value = parcJSONValue_CreateFromJSON(json); + * + * parcJSON_Release(&json); + * parcJSONValue_Release(&value); + * } + * @endcode + */ +PARCJSONValue *parcJSONValue_CreateFromJSON(PARCJSON *json); + +/** + * A convenience function to create a JSON Value containing a JSON Object representing a timeval. + * + * A new reference to the given {@link PARCJSON} instance is acquired. The json keys for the + * timespec fields are are "seconds" & "micros". + * + * @param [in] timeval A pointer to a timeval instance. + * @return A pointer to the new `PARCJSONValue`. + * + * Example: + * @code + * { + * struct timeval timeval = { .tv_sec = 1, .tv_usec = 1 }; + * PARCJSONValue *value = parcJSONValue_CreateFromTimeval(&timeval); + * + * parcJSON_Release(&json); + * parcJSONValue_Release(&value); + * } + * @endcode + */ +PARCJSONValue *parcJSONValue_CreateFromTimeval(const struct timeval *timeval); + +/** + * A convenience function to create a JSON Value containing a JSON Object representing a timespec. + * + * A new reference to the given {@link PARCJSON} instance is acquired. The json keys for the + * timespec fields are are "seconds" & "nanos". + * + * @param [in] timeval A pointer to a timespec instance. + * @return A pointer to the new `PARCJSONValue`. + * + * Example: + * @code + * { + * struct timespec timespec = { .tv_sec = 10, .tv_nsec = 0 }; + * PARCJSONValue *value = parcJSONValue_CreateFromTimespec(×pec); + * + * parcJSON_Release(&json); + * parcJSONValue_Release(&value); + * } + * @endcode + */ +PARCJSONValue *parcJSONValue_CreateFromTimespec(const struct timespec *timespec); + +/** + * Increase the number of references to a `PARCJSONValue`. + * + * Note that new `PARCJSONValue` is not created, + * only that the given `PARCJSONValue` reference count is incremented. + * Discard the reference by invoking {@link parcJSONValue_Release}. + * + * @param [in] value A pointer to the original instance. + * + * @return The value of the input parameter @p value. + * + * Example: + * @code + * { + * PARCJSONValue *x = parcJSONValue_CreateFromNull(); + * + * PARCJSONValue *x2 = parcJSONValue_Acquire(x); + * + * parcJSONValue_Release(&x); + * parcJSONValue_Release(&x2); + * } + * @endcode + * + * @see parcJSONValue_Release + */ +PARCJSONValue *parcJSONValue_Acquire(const PARCJSONValue *value); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * The contents of the dealloced memory used for the PARC object are undefined. + * Do not reference the object after the last release. + * + * @param [in,out] valuePtr A pointer to a pointer to the instance to release. + * + * Example: + * @code + * { + * PARCJSONValue *x = parcJSONValue_CreateFromNull(); + * + * parcJSONValue_Release(&x); + * } + * @endcode + */ +void parcJSONValue_Release(PARCJSONValue **valuePtr); + +/** + * Determine if two `PARCJSONValue` instances are equal. + * + * + * The following equivalence relations on non-null `PARCJSONValue` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, `parcJSONValue_Equals(x, x)` + * must return true. + * + * * It is symmetric: for any non-null reference values x and y, + * `parcJSONValue_Equals(x, y)` must return true if and only if + * `parcJSONValue_Equals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcJSONValue_Equals(x, y)` returns true and + * `parcJSONValue_Equals(y, z)` returns true, + * then `parcJSONValue_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple + * invocations of `parcJSONValue_Equals(x, y)` consistently return true or + * consistently return false. + * + * * For any non-null reference value x, `parcJSONValue_Equals(x, NULL)` must + * return false. + * + * @param [in] x A pointer to a `PARCJSONValue` instance. + * @param [in] y A pointer to a `PARCJSONValue` instance. + * @return true if the two `PARCJSONValue` instances are equal. + * + * Example: + * @code + * { + * PARCJSONValue *x = parcJSONValue_CreateFromBoolean(true); + * PARCJSONValue *y = parcJSONValue_CreateFromBoolean(true); + * + * if (parcJSONValue_Equals(x, y)) { + * // true + * } else { + * // false + * } + * } + * @endcode + */ +bool parcJSONValue_Equals(const PARCJSONValue *x, const PARCJSONValue *y); + +/** + * Produce a null-terminated string representation of the specified instance of `PARCJSONValue`. + * + * The non-null result must be freed by the caller via {@link parcMemory_Deallocate}. + * + * @param [in] value A pointer to the `PARCJSONValue` instance. + * + * @return NULL Cannot allocate memory. + * @return non-NULL A pointer to an allocated, + * null-terminated C string that must be deallocated via {@link parcMemory_Deallocate}. + * + * Example: + * @code + * { + * PARCJSONValue *instance = parcJSONValue_CreateFromString("Hello World"); + * + * char *string = parcJSONValue_ToString(instance); + * + * if (string != NULL) { + * printf("Hello: %s\n", string); + * parcMemory_Deallocate((void **)&string); + * } else { + * printf("Cannot allocate memory\n"); + * } + * + * parcJSONValue_Release(&instance); + * } + * @endcode + * + * @see parcJSONValue_BuildString + * @see parcJSONValue_Display + */ +char *parcJSONValue_ToString(const PARCJSONValue *value); + +/** + * Produce a null-terminated "compact" (minimal escaping and formatting) string representation + * of the specified instance of `PARCJSONValue`. + * + * The non-null result must be freed by the caller via {@link parcMemory_Deallocate}. + * + * @param [in] value A pointer to the `PARCJSONValue` instance. + * + * @return NULL Cannot allocate memory. + * @return non-NULL A pointer to an allocated, + * null-terminated C string that must be deallocated via {@link parcMemory_Deallocate}. + * + * Example: + * @code + * { + * PARCJSONValue *instance = parcJSONValue_CreateFromString("Hello World"); + * + * char *string = parcJSONValue_ToCompactString(instance); + * + * if (string != NULL) { + * printf("Hello: %s\n", string); + * parcMemory_Deallocate((void **)&string); + * } else { + * printf("Cannot allocate memory\n"); + * } + * + * parcJSONValue_Release(&instance); + * } + * @endcode + * + * @see parcJSONValue_BuildString + * @see parcJSONValue_Display + */ +char *parcJSONValue_ToCompactString(const PARCJSONValue *value); + +/** + * Append a representation of the specified instance to the given + * {@link PARCBufferComposer}. + * + * @param [in] value A pointer to a {@link PARCJSONParser} instance. + * @param [in,out] composer A pointer to the {@link PARCBufferComposer} instance. + * + * @return NULL Cannot allocate memory. + * @return non-NULL The given `PARCBufferComposer`. + * + * Example: + * @code + * { + * PARCBufferComposer *result = parcBufferComposer_Create(); + * + * parcJSONValue_BuildString(instance, result); + * + * PARCBuffer *string = parcBufferComposer_FinalizeBuffer(result); + * printf("JSON Value: %s\n", parcBuffer_ToString(string)); + * parcBuffer_Release(&string); + * + * parcBufferComposer_Release(&result); + * } + * @endcode + */ +PARCBufferComposer *parcJSONValue_BuildString(const PARCJSONValue *value, PARCBufferComposer *composer, bool compact); + +/** + * Parse an arbitrary JSON value. + * + * The value may be any valid JSON value, consisting of strings, numbers, objects, arrays, `true`, `false`, or `null` + * in their properly encoded string format. + * + * @param [in] parser A pointer to a {@link PARCJSONParser} instance. + * + * @return non-NULL A pointer to a valid `PARCJSONValue` instance. + * @return NULL An error occurred. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_WrapCString(" 123"); + * + * PARCJSONParser *parser = parcJSONParser_Create(buffer); + * PARCJSONValue *value = parcJSONValue_Parser(parser); + * + * parcJSONValue_Release(&value); + * parcJSONParser_Release(&parser); + * parcBuffer_Release(&buffer); + * } + * @endcode + * + * @see parcJSONValue_NumberParser + */ +PARCJSONValue *parcJSONValue_Parser(PARCJSONParser *parser); + + +/** + * Parse a JSON Object value. + * + * @param [in] parser A pointer to a {@link PARCJSONParser} instance. + * + * @return non-NULL A pointer to a valid `PARCJSONValue` instance. + * @return NULL An error occurred. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_WrapCString(" { \"name\" : 123 }"); + * + * PARCJSONParser *parser = parcJSONParser_Create(buffer); + * PARCJSONValue *value = parcJSONValue_ObjectParser(parser); + * + * parcJSONValue_Release(&value); + * parcJSONParser_Release(&parser); + * parcBuffer_Release(&buffer); + * } + * @endcode + */ +PARCJSONValue *parcJSONValue_ObjectParser(PARCJSONParser *parser); +#endif // libparc_parc_JSONValue_h diff --git a/libparc/parc/algol/parc_KeyValue.c b/libparc/parc/algol/parc_KeyValue.c new file mode 100755 index 00000000..27b20f35 --- /dev/null +++ b/libparc/parc/algol/parc_KeyValue.c @@ -0,0 +1,158 @@ +/* + * 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 <string.h> + +#include "parc_KeyValue.h" +#include "parc_Object.h" + +#include <LongBow/runtime.h> + +struct parc_key_value { + PARCObject *key; + PARCObject *value; +}; + +static void +_parcKeyValue_Destroy(PARCKeyValue **keyValuePointer) +{ + PARCKeyValue *keyValue = *keyValuePointer; + parcObject_Release(&keyValue->key); + if (keyValue->value != NULL) { + parcObject_Release(&keyValue->value); + } +} + +parcObject_ExtendPARCObject(PARCKeyValue, _parcKeyValue_Destroy, parcKeyValue_Copy, NULL, parcKeyValue_Equals, + parcKeyValue_Compare, parcKeyValue_HashCode, NULL); + +parcObject_ImplementAcquire(parcKeyValue, PARCKeyValue); + +parcObject_ImplementRelease(parcKeyValue, PARCKeyValue); + +PARCKeyValue * +parcKeyValue_Create(const PARCObject *key, + const PARCObject *value) +{ + assertNotNull(key, "Key may not be null in a KeyValue element"); + + PARCKeyValue *keyValue = parcObject_CreateInstance(PARCKeyValue); + assertNotNull(keyValue, "parcMemory_Allocate(%zu) returned NULL", sizeof(PARCKeyValue)); + + keyValue->key = parcObject_Acquire(key); + keyValue->value = NULL; + if (value != NULL) { + keyValue->value = parcObject_Acquire(value); + } + + return keyValue; +} + +PARCKeyValue * +parcKeyValue_Copy(const PARCKeyValue *source) +{ + PARCKeyValue *newKV = parcObject_CreateInstance(PARCKeyValue); + newKV->key = parcObject_Copy(source->key); + newKV->value = NULL; + if (source->value != NULL) { + newKV->value = parcObject_Copy(source->value); + } + + return newKV; +} + +void +parcKeyValue_SetValue(PARCKeyValue *keyValue, PARCObject *value) +{ + assertNotNull(keyValue, "Not a valid keyValue"); + PARCObject *oldValue = keyValue->value; + if (value != NULL) { + keyValue->value = parcObject_Acquire(value); + } else { + keyValue->value = NULL; + } + if (oldValue != NULL) { + parcObject_Release(&oldValue); + } +} + +void +parcKeyValue_SetKey(PARCKeyValue *keyValue, PARCObject *key) +{ + assertNotNull(keyValue, "Not a valid keyValue"); + PARCObject *oldKey = keyValue->key; + keyValue->key = parcObject_Acquire(key); + parcObject_Release(&oldKey); +} + +PARCObject * +parcKeyValue_GetValue(PARCKeyValue *keyValue) +{ + assertNotNull(keyValue, "Not a valid keyValue"); + return keyValue->value; +} + +PARCObject * +parcKeyValue_GetKey(PARCKeyValue *keyValue) +{ + assertNotNull(keyValue, "Not a valid keyValue"); + return keyValue->key; +} + +bool +parcKeyValue_Equals(const PARCKeyValue *a, const PARCKeyValue *b) +{ + bool result = parcObject_Equals(a->key, b->key); + if ((a->value == NULL) || (b->value == NULL)) { + result &= (a->value == b->value); // Only true if both are NULL + } else { + result &= parcObject_Equals(a->value, b->value); + } + return result; +} + +int +parcKeyValue_Compare(const PARCKeyValue *a, const PARCKeyValue *b) +{ + if (a == NULL && b == NULL) { + return 0; + } + if (a != NULL && b == NULL) { + return 1; + } + if (a == NULL && b != NULL) { + return -1; + } else { + return parcObject_Compare(a->key, b->key); + } +} + +PARCHashCode +parcKeyValue_HashCode(const PARCKeyValue *keyValue) +{ + return parcObject_HashCode(keyValue->key); +} + +bool +parcKeyValue_EqualKeys(const PARCKeyValue *a, const PARCKeyValue *b) +{ + return parcObject_Equals(a->key, b->key); +} diff --git a/libparc/parc/algol/parc_KeyValue.h b/libparc/parc/algol/parc_KeyValue.h new file mode 100755 index 00000000..a4bc3915 --- /dev/null +++ b/libparc/parc/algol/parc_KeyValue.h @@ -0,0 +1,273 @@ +/* + * 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. + */ + +/** + * @file parc_KeyValue.h + * @ingroup datastructures + * @brief A key and value tuple. + * + * The `PARCKeyValue` is a simple key-value tuple. + * + */ +#ifndef libparc_parc_KeyValue_h +#define libparc_parc_KeyValue_h + +#include "parc_Object.h" + +struct parc_key_value; + +/** + * @typedef `PARCKeyValue` + * @brief A `PARCKeyValue` is a tuple consisting of a key and a value + */ +typedef struct parc_key_value PARCKeyValue; + +/** + * Create a `PARCKeyValue` Element. The key and value must be PARCObjects. + * + * Neither the data nor the key will be copied, but referenced. + * The key may not be `NULL`. + * + * @param [in] key A pointer to the key data + * @param [in] value A pointer to the value data + * + * Example: + * @code + * { + * ... + * PARCKeyValue *kv = parcKeyValue_Create(key, value); + * ... + * parcKeyValue_Release(&kv); + * } + * @endcode + */ +PARCKeyValue *parcKeyValue_Create(const PARCObject *key, + const PARCObject *value); + +/** + * Create a copy of a `PARCKeyValue` Element. + * + * The source PARCKeyValue may not be `NULL`. + * + * @param [in] keyValue Source PARCKeyValue element + * + * Example: + * @code + * { + * ... + * PARCKeyValue *keyValueCopy = parcKeyValue_Copy(sourcKeyValue); + * ... + * parcKeyValue_Release(&keyValueCopy); + * } + * @endcode + */ +PARCKeyValue *parcKeyValue_Copy(const PARCKeyValue *keyValue); + +/** + * Acquire a reference to a `PARCKeyValue` Element. + * + * The source PARCKeyValue may not be `NULL`. + * + * @param [in] keyValue Source PARCKeyValue element + * + * Example: + * @code + * { + * ... + * PARCKeyValue *keyValue = parcKeyValue_Acquire(sourcKeyValue); + * ... + * parcKeyValue_Release(&keyValue); + * } + * @endcode + */ +PARCKeyValue *parcKeyValue_Acquire(const PARCKeyValue *keyValue); + +/** + * Release a `PARCKeyValue`. + * + * Releases a reference to a PARCKeyValue element, if it is the last reference then the + * contained key & value objects are released and the elements memory is freed. + * + * @param [in,out] keyValuePointer A pointer to the pointer to the `PARCKeyValue` to be released + * + * Example: + * @code + * ... + * PARCKeyValue *kv = parcKeyValue_Create(key, value); + * ... + * parcKeyValue_Release(&kv); + * @endcode + */ +void parcKeyValue_Release(PARCKeyValue **keyValuePointer); + +/** + * Set the value for a `PARCKeyValue`. + * The previous value will be released. + * + * @param [in,out] keyValue A pointer to the keyValue + * @param [in] value A pointer to the new value + * + * Example: + * @code + * ... + * PARCKeyValue *kv = parcKeyValue_Create(key, value); + * ... + * parcKeyValue_SetKey(kv, newKey); + * ... + * parcKeyValue_Release(&kv); + * @endcode + */ +void parcKeyValue_SetValue(PARCKeyValue *keyValue, PARCObject *value); + +/** + * Set the key for a `PARCKeyValue`. + * The previous key will be released. + * + * @param [in,out] keyValue A pointer to the `PARCKeyValue` + * @param [in] key A pointer to the new key + * + * Example: + * @code + * ... + * PARCKeyValue *kv = parcKeyValue_Create(key, value); + * ... + * parcKeyValue_SetValue(kv, newValue); + * ... + * parcKeyValue_Release(&kv); + * @endcode + */ +void parcKeyValue_SetKey(PARCKeyValue *keyValue, PARCObject *key); + +/** + * Get the value pointer for the `PARCKeyValue`. No extra reference is created. + * + * @param [in] keyValue A pointer to the `PARCKeyValue` + * + * Example: + * @code + * ... + * PARCKeyValue *kv = parcKeyValue_Create(key, value); + * ... + * PARCObject *value = parcKeyValue_GetValue(kv); + * ... + * parcKeyValue_Release(&kv); + * @endcode + */ +PARCObject *parcKeyValue_GetValue(PARCKeyValue *keyValue); + +/** + * Get the key pointer for the `PARCKeyValue`. No extra reference is created. + * + * @param [in] keyValue A pointer to the `PARCKeyValue` + * + * Example: + * @code + * ... + * PARCKeyValue *kv = parcKeyValue_Create(key, value); + * ... + * PARCObject *key = parcKeyValue_GetKey(kv); + * ... + * parcKeyValue_Release(&kv); + * @endcode + */ +PARCObject *parcKeyValue_GetKey(PARCKeyValue *keyValue); + +/** + * Check for Key equality. Return true if equal. + * + * @param [in] keyValue1 A pointer to the first `PARCKeyValue` + * @param [in] keyValue2 A pointer to the second `PARCKeyValue` + * @return true if the keyValues have the same key + * + * Example: + * @code + * ... + * PARCKeyValue *kv1 = parcKeyValue_Create(key1, value); + * PARCKeyValue *kv2 = parcKeyValue_Create(key2, value); + * ... + * if (parcKeyValue_EqualKeys(kv1, kv2)) { + * ... + * } + * ... + * parcKeyValue_Release(&kv1); + * parcKeyValue_Release(&kv2); + * @endcode + */ +bool parcKeyValue_EqualKeys(const PARCKeyValue *keyValue1, const PARCKeyValue *keyValue2); + +/** + * Check for element equality. Return true if both key & value are equal. + * + * @param [in] keyValue1 A pointer to the first `PARCKeyValue` + * @param [in] keyValue2 A pointer to the second `PARCKeyValue` + * @return true if the keys & values both have the same value + * + * Example: + * @code + * ... + * PARCKeyValue *kv1 = parcKeyValue_Create(key1, value); + * PARCKeyValue *kv2 = parcKeyValue_Create(key2, value); + * ... + * if (parcKeyValue_Equals(kv1, kv2)) { + * ... + * } + * ... + * parcKeyValue_Release(&kv1); + * parcKeyValue_Release(&kv2); + * @endcode + */ +bool parcKeyValue_Equals(const PARCKeyValue *keyValue1, const PARCKeyValue *keyValue2); + +/** + * Compare PARCKeyValue elements. Return an int result based on key compare only. + * + * @param [in] keyValue1 A pointer to the first `PARCKeyValue` + * @param [in] keyValue2 A pointer to the second `PARCKeyValue` + * @return parcObject_Compare(keyValue1->key, keyValue2->key) + * + * Example: + * @code + * ... + * PARCKeyValue *kv1 = parcKeyValue_Create(key1, value); + * PARCKeyValue *kv2 = parcKeyValue_Create(key2, value); + * ... + * if (parcKeyValue_Compare(kv1, kv2) > 0) { + * ... + * } + * ... + * parcKeyValue_Release(&kv1); + * parcKeyValue_Release(&kv2); + * @endcode + */ +int parcKeyValue_Compare(const PARCKeyValue *keyValue1, const PARCKeyValue *keyValue2); + +/** + * Return the HashCode of the PARCKeyValue key. + * + * @param [in] keyValue A pointer to the first `PARCKeyValue` + * @return parcObject_HashCode(keyValue->key); + * + * Example: + * @code + * ... + * PARCKeyValue *keyValue = parcKeyValue_Create(key1, value); + * ... + * PARCHashCode *hashCode = parcKeyValue_HashCode(keyValue); + * ... + * parcKeyValue_Release(&keyValue); + * @endcode + */ +PARCHashCode parcKeyValue_HashCode(const PARCKeyValue *keyValue); +#endif // libparc_parc_KeyValue_h diff --git a/libparc/parc/algol/parc_KeyedElement.c b/libparc/parc/algol/parc_KeyedElement.c new file mode 100755 index 00000000..a55de891 --- /dev/null +++ b/libparc/parc/algol/parc_KeyedElement.c @@ -0,0 +1,78 @@ +/* + * 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 <string.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_KeyedElement.h> +#include <parc/algol/parc_Memory.h> + +struct parc_keyed_element { + size_t keylen; + void *key; + void *element; +}; + +PARCKeyedElement * +parcKeyedElement_Create(void *data, const void *key, size_t keylen) +{ + PARCKeyedElement *keyedElement = parcMemory_Allocate(sizeof(PARCKeyedElement)); + assertNotNull(keyedElement, "parcMemory_Allocate(%zu) returned NULL", sizeof(PARCKeyedElement)); + keyedElement->element = data; + keyedElement->key = parcMemory_Allocate(sizeof(PARCKeyedElement)); + assertNotNull(keyedElement->key, "parcMemory_Allocate(%zu) returned NULL", sizeof(PARCKeyedElement)); + memcpy(keyedElement->key, key, keylen); + keyedElement->keylen = keylen; + return keyedElement; +} + +void +parcKeyedElement_Destroy(PARCKeyedElement **keyedElementPointer) +{ + parcMemory_Deallocate((void **) &((*keyedElementPointer)->key)); + parcMemory_Deallocate((void **) keyedElementPointer); + *keyedElementPointer = NULL; +} + +void +parcKeyedElement_SetData(PARCKeyedElement *keyedElement, void *data) +{ + keyedElement->element = data; +} + +void * +parcKeyedElement_GetData(PARCKeyedElement *keyedElement) +{ + return keyedElement->element; +} + +void * +parcKeyedElement_GetKey(PARCKeyedElement *keyedElement) +{ + return keyedElement->key; +} + +size_t +parcKeyedElement_GetKeyLen(PARCKeyedElement *keyedElement) +{ + return keyedElement->keylen; +} diff --git a/libparc/parc/algol/parc_KeyedElement.h b/libparc/parc/algol/parc_KeyedElement.h new file mode 100755 index 00000000..e794360f --- /dev/null +++ b/libparc/parc/algol/parc_KeyedElement.h @@ -0,0 +1,128 @@ +/* + * 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. + */ + +/** + * @file parc_KeyedElement.h + * @ingroup datastructures + * @brief A Pointer and a Key + * The `PARCKeyedElement` is a simple pointer and key tuple. + * + * + * Example: + * @code + * <#example#> + * @endcode + */ +#ifndef libparc_parc_KeyedElement_h +#define libparc_parc_KeyedElement_h + +#include <stdint.h> +#include <stdlib.h> + +struct parc_keyed_element; + +/** + * A `PARCKeyedElement` is a tuple consisting of an address and a key+len. + * + * Example: + * @code + * <#example#> + * @endcode + */ +typedef struct parc_keyed_element PARCKeyedElement; + +/** + * Create a `PARCKeyedElement`. Note that the key will be copied (size keylen) while the data will just be + * referenced. The key copy will be released when the KeyedElement is destroyed. + * + * @param [in] data The data we want to associate with a key. + * @param [in] key A pointer to the key data (will be copied). + * @param [in] keylen The length of the keydata. + * @return The new instance of `PARCKeyedElement`. + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCKeyedElement *parcKeyedElement_Create(void *data, const void *key, size_t keylen); + +/** + * Destroy a `PARCKeyedElement`. + * + * If the Free functions were passed to the constructor they will be called if + * not NULL. * + * @param [in,out] keyedElementPointer A pointer to the pointer to the `PARCKeyedElement` to be destroyed. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void parcKeyedElement_Destroy(PARCKeyedElement **keyedElementPointer); +/** + * Set the data of a `PARCKeyedElement`. + * + * @param [in,out] keyedElement The pointer to the `PARCKeyedElement` whose data will be reset to @p data. + * @param [in] data The data to put be into @p keyedElement + * + * Example: + * @code + * <#example#> + * @endcode + */ +void parcKeyedElement_SetData(PARCKeyedElement *keyedElement, void *data); + +/** + * Get the data of a `PARCKeyedElement`. + * + * @param [in] keyedElement The pointer to the `PARCKeyedElement` whose data will be retrieved. + * @return A pointer to the retrieved data. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void *parcKeyedElement_GetData(PARCKeyedElement *keyedElement); + +/** + * Retrieve the key of a `PARCKeyedElement`. + * + * @param [in] keyedElement A pointer to the `PARCKeyedElement` whose key should be retreieved. + * + * @return A pointer to the `PARCKeyedElement`'s key. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void *parcKeyedElement_GetKey(PARCKeyedElement *keyedElement); + +/** + * Return the size of the `PARCKeyedElement`'s key. + * + * @param [in] keyedElement A pointer to the `PARCKeyedElement` whose key length should be returned. + * + * @return The length of the retrieved key. + * + * Example: + * @code + * <#example#> + * @endcode + */ +size_t parcKeyedElement_GetKeyLen(PARCKeyedElement *keyedElement); +#endif // libparc_parc_KeyedElement_h diff --git a/libparc/parc/algol/parc_LinkedList.c b/libparc/parc/algol/parc_LinkedList.c new file mode 100644 index 00000000..d220e223 --- /dev/null +++ b/libparc/parc/algol/parc_LinkedList.c @@ -0,0 +1,705 @@ +/* + * 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 <LongBow/runtime.h> + +#include <stdio.h> +#include <sys/queue.h> + +#include <parc/algol/parc_LinkedList.h> + +#include <parc/algol/parc_DisplayIndented.h> +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_Memory.h> + +static PARCListInterface *PARCLinkedListAsPARCList = &(PARCListInterface) { + .Add = (bool (*)(void *, void *))parcLinkedList_Append, + .AddAtIndex = (void (*)(void *, int index, PARCObject *))parcLinkedList_InsertAtIndex, + .AddCollection = (bool (*)(void *, PARCCollection *))NULL, + .AddCollectionAtIndex = (bool (*)(void *, int index, PARCCollection *))NULL, + .Clear = (void (*)(void *))NULL, + .Contains = (bool (*)(const void *, const PARCObject *))parcLinkedList_Contains, + .ContainsCollection = (bool (*)(const void *, const PARCCollection *))NULL, + .Copy = (void * (*)(const PARCList *))parcLinkedList_Copy, + .Destroy = (void (*)(void **))parcLinkedList_Release, + .Equals = (bool (*)(const void *, const void *))parcLinkedList_Equals, + .GetAtIndex = (PARCObject * (*)(const void *, size_t))parcLinkedList_GetAtIndex, + .HashCode = (PARCHashCode (*)(const void *))parcLinkedList_HashCode, + .IndexOf = (size_t (*)(const void *, const PARCObject *element))NULL, + .IsEmpty = (bool (*)(const void *))parcLinkedList_IsEmpty, + .LastIndexOf = (size_t (*)(void *, const PARCObject *element))NULL, + .Remove = (bool (*)(void *, const PARCObject *element))parcLinkedList_Remove, + .RemoveAtIndex = (PARCObject * (*)(PARCList *, size_t))parcLinkedList_RemoveAtIndex, + .RemoveCollection = (bool (*)(void *, const PARCCollection *))NULL, + .RetainCollection = (bool (*)(void *, const PARCCollection *))NULL, + .SetAtIndex = (PARCObject * (*)(void *, size_t index, PARCObject *))parcLinkedList_SetAtIndex, + .Size = (size_t (*)(const void *))parcLinkedList_Size, + .SubList = (PARCList * (*)(const void *, size_t, size_t))NULL, + .ToArray = (void** (*)(const void *))NULL, +}; + +typedef struct parc_linkedlist_node { + PARCObject *object; + struct parc_linkedlist_node *previous; + struct parc_linkedlist_node *next; +} _PARCLinkedListNode; + +struct parc_linkedlist { + _PARCLinkedListNode *head; + _PARCLinkedListNode *tail; + size_t size; +}; + +static inline _PARCLinkedListNode * +_parcLinkedListNode_getByIndex(const PARCLinkedList *list, size_t index) +{ + _PARCLinkedListNode *node = list->head; + while (index-- && node != NULL) { + node = node->next; + } + return node; +} + +static inline _PARCLinkedListNode * +_parcLinkedListNode_getByValue(const PARCLinkedList *list, const PARCObject *value) +{ + _PARCLinkedListNode *node = list->head; + while (node != NULL && parcObject_Equals(node->object, value) == false) { + node = node->next; + } + return node; +} + +static bool +_parcLinkedListNode_IsValid(const _PARCLinkedListNode *node) +{ + bool result = false; + + if (node != NULL) { + if (node->object != NULL) { + if (parcObject_IsValid(node->object)) { + if (node->previous) { + if (node->previous->next == node) { + if (parcObject_IsValid(node->previous->object)) { + result = true; + } + } + } else { + result = true; + } + if (node->next != NULL) { + if (node->next->previous == node) { + if (parcObject_IsValid(node->next->object)) { + result = true; + } + } + } else { + result = true; + } + } + } + } + + return result; +} + +static inline _PARCLinkedListNode * +_parcLinkedListNode_Create(const PARCObject *object, _PARCLinkedListNode *previous, _PARCLinkedListNode *next) +{ + _PARCLinkedListNode *result = parcMemory_Allocate(sizeof(_PARCLinkedListNode)); + if (result != NULL) { + result->object = parcObject_Acquire(object); + result->next = next; + result->previous = previous; + } + + return result; +} + +static void +_parcLinkedListIterator_IsValid(const _PARCLinkedListNode *node) +{ + if (node != NULL) { + assertTrue(_parcLinkedListNode_IsValid(node), "node is invalid"); + } +} + +static void +_parcLinkedListNode_Destroy(PARCLinkedList *list __attribute__((unused)), _PARCLinkedListNode **nodePtr) +{ + _PARCLinkedListNode *node = *nodePtr; + + parcObject_Release(&node->object); + parcMemory_Deallocate((void **) nodePtr); +} + +static bool +_parcLinkedList_Destructor(PARCLinkedList **listPtr) +{ + PARCLinkedList *list = *listPtr; + + _PARCLinkedListNode *next = NULL; + + for (_PARCLinkedListNode *node = list->head; node != NULL; node = next) { + next = node->next; + _parcLinkedListNode_Destroy(list, &node); + } + return true; +} + +static _PARCLinkedListNode * +_parcLinkedIterator_Init(PARCLinkedList *list __attribute__((unused))) +{ + return NULL; +} + +static bool +_parcLinkedListNode_Fini(PARCLinkedList *list __attribute__((unused)), const _PARCLinkedListNode *node __attribute__((unused))) +{ + return true; +} + +static struct parc_linkedlist_node * +_parcLinkedListNode_Next(PARCLinkedList *list __attribute__((unused)), const _PARCLinkedListNode *node) +{ + struct parc_linkedlist_node *result = NULL; + + if (node == NULL) { + result = list->head; + } else { + assertTrue(_parcLinkedListNode_IsValid(node), "node is invalid"); + trapOutOfBoundsIf(node->next == NULL, "No more elements."); + result = node->next; + } + + assertTrue(_parcLinkedListNode_IsValid(result), "result is invalid"); + parcObject_OptionalAssertValid(result->object); + + return result; +} + +static inline PARCObject * +_parcLinkedListNode_Delete(PARCLinkedList *list, _PARCLinkedListNode *node) +{ + PARCObject *result = node->object; + + list->size--; + + if (node == list->head) { + list->head = node->next; + } + if (node == list->tail) { + list->tail = node->previous; + } + if (node->previous) { + node->previous->next = node->next; + } + if (node->next) { + node->next->previous = node->previous; + } + + parcMemory_Deallocate((void **) &node); + + return result; +} + +static void +_parcLinkedListNode_Remove(PARCLinkedList *list, _PARCLinkedListNode **nodePtr) +{ + parcLinkedList_OptionalAssertValid(list); + + _PARCLinkedListNode *node = *nodePtr; + + if (node != NULL) { + *nodePtr = node->previous; + + PARCObject *object = _parcLinkedListNode_Delete(list, node); + parcObject_Release(&object); + + parcLinkedList_OptionalAssertValid(list); + } +} + +static bool +_parcLinkedListNode_HasNext(PARCLinkedList *list, const _PARCLinkedListNode *node) +{ + bool result = false; + + if (node == NULL) { + result = (list->head != NULL); + if (result) { + assertTrue(_parcLinkedListNode_IsValid(list->head), "node is invalid"); + } + } else { + result = node->next != NULL; + if (result) { + assertTrue(_parcLinkedListNode_IsValid(node->next), "node is invalid"); + } + } + + + return result; +} + +static void * +_parcLinkedListNode_Element(PARCLinkedList *list __attribute__((unused)), const _PARCLinkedListNode *node) +{ + return node->object; +} + +parcObject_Override(PARCLinkedList, PARCObject, + .destructor = (PARCObjectDestructor *) _parcLinkedList_Destructor, + .copy = (PARCObjectCopy *) parcLinkedList_Copy, + .equals = (PARCObjectEquals *) parcLinkedList_Equals, + .hashCode = (PARCObjectHashCode *) parcLinkedList_HashCode, + .display = (PARCObjectDisplay *) parcLinkedList_Display); + +PARCIterator * +parcLinkedList_CreateIterator(PARCLinkedList *list) +{ + PARCIterator *iterator = parcIterator_Create(list, + (void *(*)(PARCObject *))_parcLinkedIterator_Init, + (bool (*)(PARCObject *, void *))_parcLinkedListNode_HasNext, + (void *(*)(PARCObject *, void *))_parcLinkedListNode_Next, + (void (*)(PARCObject *, void **))_parcLinkedListNode_Remove, + (void *(*)(PARCObject *, void *))_parcLinkedListNode_Element, + (void (*)(PARCObject *, void *))_parcLinkedListNode_Fini, + (void (*)(const void *))_parcLinkedListIterator_IsValid); + + return iterator; +} + +PARCLinkedList * +parcLinkedList_Create(void) +{ + PARCLinkedList *result = parcObject_CreateInstance(PARCLinkedList); + + if (result != NULL) { + result->head = NULL; + result->tail = NULL; + result->size = 0; + } + return result; +} + +bool +parcLinkedList_IsValid(const PARCLinkedList *list) +{ + bool result = false; + + if (list != NULL) { + if (parcObject_IsValid(list)) { + if (list->size > 0) { + if (list->head != NULL) { + if (list->tail != NULL) { + result = true; + for (_PARCLinkedListNode *node = list->head; node != NULL; node = node->next) { + if (_parcLinkedListNode_IsValid(node) == false) { + result = false; + break; + } + } + } + } + } else { + if (list->head == NULL) { + if (list->tail == NULL) { + result = true; + } + } + } + } + } + + return result; +} + +void +parcLinkedList_AssertValid(const PARCLinkedList *instance) +{ + assertTrue(parcLinkedList_IsValid(instance), + "PARCLinkedList is not valid."); +} + +parcObject_ImplementAcquire(parcLinkedList, PARCLinkedList); + +parcObject_ImplementRelease(parcLinkedList, PARCLinkedList); + +PARCLinkedList * +parcLinkedList_Copy(const PARCLinkedList *list) +{ + PARCLinkedList *result = parcLinkedList_Create(); + + struct parc_linkedlist_node *node = list->head; + + while (node != NULL) { + parcLinkedList_Append(result, node->object); + node = node->next; + } + + return result; +} + +bool +parcLinkedList_Contains(const PARCLinkedList *list, const PARCObject *element) +{ + bool result = false; + + struct parc_linkedlist_node *node = list->head; + + while (node != NULL) { + if (parcObject_Equals(node->object, element)) { + result = true; + break; + } + node = node->next; + } + + return result; +} + +PARCLinkedList * +parcLinkedList_Append(PARCLinkedList *list, const PARCObject *element) +{ + _PARCLinkedListNode *node = _parcLinkedListNode_Create(element, list->tail, NULL); + + if (list->tail == NULL) { + list->tail = node; + } else { + list->tail->next = node; + list->tail = node; + } + + if (list->head == NULL) { + list->head = list->tail; + } + + list->size++; + + return list; +} + +PARCLinkedList * +parcLinkedList_AppendAll(PARCLinkedList *list, const PARCLinkedList *other) +{ + PARCIterator *iterator = parcLinkedList_CreateIterator((PARCLinkedList *) other); + while (parcIterator_HasNext(iterator)) { + PARCObject *object = parcIterator_Next(iterator); + parcLinkedList_Append(list, object); + } + parcIterator_Release(&iterator); + + return list; +} + +PARCLinkedList * +parcLinkedList_Prepend(PARCLinkedList *list, const PARCObject *element) +{ + _PARCLinkedListNode *node = _parcLinkedListNode_Create(element, NULL, list->head); + + if (list->head == NULL) { + list->head = node; + } else { + list->head->previous = node; + list->head = node; + } + + if (list->tail == NULL) { + list->tail = list->head; + } + list->size++; + + parcLinkedList_OptionalAssertValid(list); + + return list; +} + +PARCObject * +parcLinkedList_RemoveFirst(PARCLinkedList *list) +{ + PARCObject *result = NULL; + + if (list->head != NULL) { + _PARCLinkedListNode *node = list->head; + result = _parcLinkedListNode_Delete(list, node); + } + + parcLinkedList_OptionalAssertValid(list); + + return result; +} + +PARCObject * +parcLinkedList_RemoveLast(PARCLinkedList *list) +{ + PARCObject *result = NULL; + + if (list->tail != NULL) { + _PARCLinkedListNode *node = list->tail; + result = _parcLinkedListNode_Delete(list, node); + } + + parcLinkedList_OptionalAssertValid(list); + return result; +} + +bool +parcLinkedList_Remove(PARCLinkedList *list, const PARCObject *element) +{ + assertTrue(element != NULL, "Element must not be NULL"); + bool result = false; + + _PARCLinkedListNode *node = _parcLinkedListNode_getByValue(list, element); + if (node != NULL) { + PARCObject *e = _parcLinkedListNode_Delete(list, node); + parcObject_Release(&e); + result = true; + } + + parcLinkedList_OptionalAssertValid(list); + + return result; +} + +PARCObject * +parcLinkedList_RemoveAtIndex(PARCLinkedList *list, size_t index) +{ + PARCObject *result = NULL; + + _PARCLinkedListNode *node = _parcLinkedListNode_getByIndex(list, index); + if (node != NULL) { + result = _parcLinkedListNode_Delete(list, node); + } + + return result; +} + +PARCObject * +parcLinkedList_GetFirst(const PARCLinkedList *list) +{ + PARCObject *result = NULL; + + if (list->head != NULL) { + _PARCLinkedListNode *node = list->head; + result = node->object; + } + return result; +} + +PARCObject * +parcLinkedList_GetLast(const PARCLinkedList *list) +{ + PARCObject *result = NULL; + + if (list->tail != NULL) { + _PARCLinkedListNode *node = list->tail; + result = node->object; + } + return result; +} + +PARCHashCode +parcLinkedList_HashCode(const PARCLinkedList *list) +{ + PARCHashCode result = 0; + + _PARCLinkedListNode *node = list->head; + if (node != NULL) { + while (node != NULL) { + result += parcObject_HashCode(node->object); + node = node->next; + } + } + + return result; +} + +size_t +parcLinkedList_Size(const PARCLinkedList *list) +{ + return list->size; +} + +bool +parcLinkedList_IsEmpty(const PARCLinkedList *list) +{ + return (parcLinkedList_Size(list) == 0); +} + +static void +_parcLinkedList_InsertInitialNode(PARCLinkedList *list, const PARCObject *element) +{ + _PARCLinkedListNode *newNode = _parcLinkedListNode_Create(element, NULL, NULL); + list->head = newNode; + list->tail = newNode; +} + +PARCLinkedList * +parcLinkedList_InsertAtIndex(PARCLinkedList *list, size_t index, const PARCObject *element) +{ + if (index == 0) { + if (list->head == NULL) { + _parcLinkedList_InsertInitialNode(list, element); + } else { + _PARCLinkedListNode *newNode = _parcLinkedListNode_Create(element, NULL, list->head); + + list->head->previous = newNode; + list->tail = list->head; + list->head = newNode; + } + + list->size++; + } else if (index == list->size) { + _PARCLinkedListNode *node = list->tail; + node->next = _parcLinkedListNode_Create(element, node, NULL); + list->tail = node->next; + list->size++; + } else { + _PARCLinkedListNode *node = list->head; + while (index-- && node->next != NULL) { + node = node->next; + } + _PARCLinkedListNode *newNode = _parcLinkedListNode_Create(element, node->previous, node); + + node->previous->next = newNode; + node->previous = newNode; + list->size++; + } + + parcLinkedList_OptionalAssertValid(list); + return list; +} + +PARCObject * +parcLinkedList_SetAtIndex(PARCLinkedList *list, size_t index, PARCObject *element) +{ + PARCObject *result = NULL; + + if (index > (parcLinkedList_Size(list) - 1)) { + trapOutOfBounds(index, "[0, %zd]", parcLinkedList_Size(list) - 1); + } + + _PARCLinkedListNode *node = _parcLinkedListNode_getByIndex(list, index); + if (node != NULL) { + result = node->object; + node->object = parcObject_Acquire(element); + } + return result; +} + +PARCObject * +parcLinkedList_GetAtIndex(const PARCLinkedList *list, size_t index) +{ + if (index > (parcLinkedList_Size(list) - 1)) { + trapOutOfBounds(index, "[0, %zd]", parcLinkedList_Size(list) - 1); + } + + _PARCLinkedListNode *node = _parcLinkedListNode_getByIndex(list, index); + return (node == NULL) ? NULL : node->object; +} + +bool +parcLinkedList_Equals(const PARCLinkedList *x, const PARCLinkedList *y) +{ + if (x == y) { + return true; + } + if (x == NULL || y == NULL) { + return false; + } + + if (x->size == y->size) { + _PARCLinkedListNode *xNode = x->head; + _PARCLinkedListNode *yNode = y->head; + + while (xNode != NULL) { + if (parcObject_Equals(xNode->object, yNode->object) == false) { + return false; + } + xNode = xNode->next; + yNode = yNode->next; + } + return true; + } + return false; +} + +void +parcLinkedList_Display(const PARCLinkedList *list, const int indentation) +{ + if (list == NULL) { + parcDisplayIndented_PrintLine(indentation, "PARCLinkedList@NULL"); + } else { + parcDisplayIndented_PrintLine(indentation, "PARCLinkedList@%p { .size=%016zp, .head=%016zp, .tail=%016zp", + (void *) list, list->size, list->head, list->tail); + + _PARCLinkedListNode *node = list->head; + + while (node != NULL) { + parcDisplayIndented_PrintLine(indentation + 1, + "%016zp { .previous=%016zp, %016zp, .next=%016zp }", + node, node->previous, node->object, node->next); + parcObject_Display(node->object, indentation + 2); + node = node->next; + } + + parcDisplayIndented_PrintLine(indentation, "}\n"); + } +} + +bool +parcLinkedList_SetEquals(const PARCLinkedList *x, const PARCLinkedList *y) +{ + bool result = false; + + if (x->size == y->size) { + for (size_t i = 0; i < x->size; i++) { + PARCObject *xObject = parcLinkedList_GetAtIndex(x, i); + for (size_t j = 0; j < x->size; j++) { + PARCObject *yObject = parcLinkedList_GetAtIndex(y, j); + if (parcObject_Equals(xObject, yObject) == false) { + break; + } + } + } + result = true; + } + + return result; +} + +PARCList * +parcLinkedList_AsPARCList(PARCLinkedList *list) +{ + PARCList *result = parcList_Create(list, PARCLinkedListAsPARCList); + return result; +} + +void +parcLinkedList_ApplyImpl(PARCLinkedList *list, void (*function)(PARCObject *, const void *), const void *parameter) +{ + PARCIterator *iterator = parcLinkedList_CreateIterator(list); + + while (parcIterator_HasNext(iterator)) { + PARCObject *object = parcIterator_Next(iterator); + function(object, parameter); + } + + parcIterator_Release(&iterator); +} diff --git a/libparc/parc/algol/parc_LinkedList.h b/libparc/parc/algol/parc_LinkedList.h new file mode 100644 index 00000000..7babff79 --- /dev/null +++ b/libparc/parc/algol/parc_LinkedList.h @@ -0,0 +1,698 @@ +/* + * 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. + */ + +/** + * @file parc_LinkedList.h + * @ingroup datastructures + * @brief PARC Double-ended Queue (Deque) + * + */ +#ifndef libparc_parc_LinkedList_h +#define libparc_parc_LinkedList_h +#include <stdbool.h> +#include <stdint.h> + +#include <parc/algol/parc_List.h> +#include <parc/algol/parc_HashCode.h> +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_Iterator.h> + +struct parc_linkedlist; +/** + * A simple linked list. + * + * @see {@link parcLinkedList_Create} + */ +typedef struct parc_linkedlist PARCLinkedList; + +/** + * Create a `PARCLinkedList` instance with the default element equality and copy functions. + * + * The queue is created with no elements. + * + * The default element equals function is used by the `{@link parcLinkedList_Equals} function and + * simply compares the values using the `==` operator. + * + * @return non-NULL A pointer to a `PARCLinkedList` instance. + * + * Example: + * @code + * <#example#> + * @endcode + * + */ +PARCLinkedList *parcLinkedList_Create(void); + +#ifdef PARCLibrary_DISABLE_VALIDATION +# define parcLinkedList_OptionalAssertValid(_instance_) +#else +# define parcLinkedList_OptionalAssertValid(_instance_) parcLinkedList_AssertValid(_instance_) +#endif +void parcLinkedList_AssertValid(const PARCLinkedList *list); + +/** + * Create a new instance of PARCIterator that iterates through the given PARCLinkedList. + * The returned value must be released via {@link parcIterator_Release}. + * + * @param [in] list A pointer to a valid `PARCLinkedList`. + * + * @see parcIterator_Release + * + * Example: + * @code + * { + * PARCIterator *iterator = parcLinkedList_CreateIterator(list); + * + * while (parcIterator_HasNext(iterator)) { + * PARCObject *object = parcIterator_Next(iterator); + * } + * + * parcIterator_Release(&iterator); + * } + * @endcode + */ +PARCIterator *parcLinkedList_CreateIterator(PARCLinkedList *list); + +/** + * Acquire a new reference to an instance of `PARCLinkedList`. + * + * The reference count to the instance is incremented. + * + * @param [in] list The instance of `PARCLinkedList` to which to refer. + * + * @return The same value as the input parameter @p deque + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCLinkedList *parcLinkedList_Acquire(const PARCLinkedList *list); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's interface will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] listPtr A pointer to a pointer to the instance of `PARCLinkedList` to release. + * + * + * Example: + * @code + * { + * PARCLinkedList *buffer = parcLinkedList_Create(10); + * + * parcLinkedList_Release(&buffer); + * } + * @endcode + */ +void parcLinkedList_Release(PARCLinkedList **listPtr); + +/** + * Copy a a `PARCLinkedList` to another. + * + * @param [in] list A pointer to an instance of `PARCLinkedList` + * + * @return A pointer to a copy of the original instance of `PARCLinkedList` + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +PARCLinkedList *parcLinkedList_Copy(const PARCLinkedList *list); + +/** + * Determine if an instance of `PARCLinkedList` is valid. + * + * Valid means the internal state of the type is consistent with its required current or future behaviour. + * This may include the validation of internal instances of types. + * + * @param [in] list A pointer to a `PARCLinkedList` instance. + * + * @return true The instance is valid. + * @return false The instance is not valid. + * + * Example: + * @code + * { + * PARCLinkedList *instance = parcLinkedList_Create(); + * + * if (parcLinkedList_IsValid(instance)) { + * printf("Instance is valid.\n"); + * } + * } + * @endcode + */ +bool parcLinkedList_IsValid(const PARCLinkedList *list); + +/** + * Returns a hash code value for the given instance. + * + * The general contract of the `HashCode` function is: + * + * Whenever it is invoked on the same instance more than once during an execution of an application, + * the `HashCode` function must consistently return the same value, + * provided no information in the instance is modified. + * + * This value need not remain consistent from one execution of an application to another execution of the same application. + * If two instances are equal according to the `Equals` function, + * then calling the `HashCode` function on each of the two instances must produce the same result. + * + * It is not required that if two instances are unequal according to the `Equals` function, + * then calling the `HashCode` function + * on each of the two objects must produce distinct integer results. + * + * @param [in] instance A pointer to the `PARCLinkedList` instance. + * + * @return The hashcode for the given instance. + * + * Example: + * @code + * { + * PARCLinkedList *buffer = parcLinkedList_Allocate(10); + * PARCHashCode hash = parcLinkedList_HashCode(buffer); + * parcLinkedList_Release(&buffer); + * } + * @endcode + */ +PARCHashCode parcLinkedList_HashCode(const PARCLinkedList *instance); + +/** + * Returns true if the given `PARCLinkedList` contains the specified `PARCObject`. + * + * The semantics are such that the function returns `true` if and only if the `PARCLinkedList` + * list contains at least one `PARCObject` _o_ such that PARCObject_Equals(_o_, object) is true. + * + * @param [in] list A pointer to a valid `PARCLinkedList` instance. + * @param [in] object A pointer to a valid `PARCObject` instance. + * + * @return true The given `PARCLinkedList` contains the specified `PARCObject`. + * @return false The given `PARCLinkedList` does not contain the specified `PARCObject`. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +bool parcLinkedList_Contains(const PARCLinkedList *list, const PARCObject *object); + +/** + * Append an element to the tail end of the specified `PARCLinkedList` + * + * @param [in] list A pointer to the instance of `PARCLinkedList` to which the element will be appended + * @param [in] element A pointer to the element to be appended to the instance of `PARCLinkedList` + * + * @return non NULL A pointer to the specific instance of `PARCLinkedList` + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCLinkedList *parcLinkedList_Append(PARCLinkedList *list, const PARCObject *element); + +/** + * Append each element from the PARCLinkedList @p other to @p list. + * + * @param [in] list A pointer to a valid PARCLinkedList instance that will be receive each element from @p other. + * @param [in] other A pointer to a valid PARCLinkedList instance containing the elements to append to @p list. + * + * @return The value of @p list. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +PARCLinkedList *parcLinkedList_AppendAll(PARCLinkedList *list, const PARCLinkedList *other); + +/** + * Prepend an element to the head end of the specified `PARCLinkedList` + * + * @param [in] list A pointer to the instance of `PARCLinkedList` to which the element will be prepended + * @param [in] element A pointer to the element to be appended to the instance of `PARCLinkedList` + * + * @return non NULL A pointer to the specific instance of `PARCLinkedList` + * + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCLinkedList *parcLinkedList_Prepend(PARCLinkedList *list, const PARCObject *element); + +/** + * Return the first element of the specified `PARCLinkedList` and remove it from the list. + * The element's reference count is not modified, + * the caller must release the returned element when it is finished with it. + * + * @param [in] list A pointer to the instance of `PARCLinkedList` from which the first element will be returned and removed + * + * @return non NULL A pointer to the element removed + * + * Example: + * @code + * <#example#> + * @endcode + * + */ +PARCObject *parcLinkedList_RemoveFirst(PARCLinkedList *list); + +/** + * Remove the last element in the queue and return it. + * The element's reference count is not modified, + * the caller must release the returned element when it is finished with it. + * + * @param [in] list A pointer to the instance of `PARCLinkedList` from which the last element will be removed and returned. + * + * @return non-NULL A pointer to the element removed + * + * Example: + * @code + * <#example#> + * @endcode + * + */ +PARCObject *parcLinkedList_RemoveLast(PARCLinkedList *list); + +/** + * Remove the first occurrence of the given element from the specified 'PARCLinkedList'. + * The element's reference count is decremented. + * + * @param [in] element the element to remove + * @return true if the element was found in the list and successfully removed + * + * Example: + * @code + * { + * PARCLinkedList *list = parcLinkedList_Create(); + * + * PARCBuffer *buffer = parcBuffer_WrapCString("1"); + * parcLinkedList_Append(list, buffer); + * parcBuffer_Release(&buffer); + * // ... + * PARCBuffer *actual = parcLinkedList_Remove(list, buffer); + * parcBuffer_Release(&actual); + * + * // ... + * + * parcLinkedList_Release(&list); + * } + * @endcode + */ +bool parcLinkedList_Remove(PARCLinkedList *list, const PARCObject *element); + +/** + * Removes the element at the specified position in this list. + * + * Shifts all subsequent elements to the left (subtracts one from their indices). + * Return the element that was removed from the list without modifying the reference count. + * The caller must eventually release the returned value. + * + * @param [in] list A pointer to the instance of `PARCLinkedList` from which the element will be removed. + * @param [in] index The index (origin 0) of the element to remove. + * + * @return The element that was removed from the list. + * + * Example: + * @code + * { + * PARCLinkedList *list = parcLinkedList_Create(); + * // add elements to the list. + * + * parcLinkedList_RemoveAtIndex(list, 2); // remove the 3rd element in the list. + * + * // ... + * + * parcLinkedList_Release(&list); + * } + * @endcode + */ +PARCObject *parcLinkedList_RemoveAtIndex(PARCLinkedList *list, size_t index); + +/** + * Return the first element of the specified `PARCLinkedList` but do NOT remove it from the queue + * + * @param [in] list A pointer to the instance of `PARCLinkedList` from which the first element will be returned + * + * @return non NULL A pointer to the first element + * + * Example: + * @code + * <#example#> + * @endcode + * + */ +PARCObject *parcLinkedList_GetFirst(const PARCLinkedList *list); + +/** + * Return the last element of the specified `PARCLinkedList` but do NOT remove it from the queue + * + * @param [in] list A pointer to the instance of `PARCLinkedList` from which the last element will be returned + * + * @return non NULL A pointer to the last element + * + * Example: + * @code + * <#example#> + * @endcode + * + */ +PARCObject *parcLinkedList_GetLast(const PARCLinkedList *list); + +/** + * Return the size of the specified queue + * + * @param [in] list A pointer to the instance of `PARCLinkedList` + * + * @return `size_t` The size of the queue + * + * Example: + * @code + * <#example#> + * @endcode + * + */ +size_t parcLinkedList_Size(const PARCLinkedList *list); + +/** + * Return True if the `PARCLinkedList` is empty or False if not. + * + * @param [in] list A pointer to the instance of `PARCLinkedList` + * + * @return bool True if the `PARCLinkedList` is empty or False if not. + * + * Example: + * @code + * <#example#> + * @endcode + * + */ +bool parcLinkedList_IsEmpty(const PARCLinkedList *list); + +/** + * Get a pointer to the specified element. + * + * @param [in] list A pointer to a `PARCLinkedList` instance. + * @param [in] index The index of the element to be retrieved. + * + * @throws `trapOutOfBounds` + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCObject *parcLinkedList_GetAtIndex(const PARCLinkedList *list, size_t index); + +/** + * Replace the element at the specified position in this list with the given element. + * + * @param [in] list A pointer to a `PARCLinkedList` instance. + * @param [in] index The index of the element to be replaced. + * @param [in] element A pointer to a valid PARCObject instance that will replace the current element at @p index. + * + * @throws `trapOutOfBounds` + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCObject *parcLinkedList_SetAtIndex(PARCLinkedList *list, size_t index, PARCObject *element); + +/** + * Determine if two `PARCLinkedList` instances are equal. + * + * This function implements the following equivalence relations on non-null `PARCLinkedList` instances: + * + * * It is reflexive: for any non-null reference value x, `parcLinkedList_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `parcLinkedList_Equals(x, y)` must return true if and only if + * `parcLinkedList_Equals(y x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcLinkedList_Equals(x, y)` returns true and + * `parcLinkedList_Equals(y, z)` returns true, + * then `parcLinkedList_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `parcLinkedList_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `parcLinkedList_Equals(x, NULL)` must return false. + * + * Two `PARCLinkedList` instances with different element equality functions are always unequal. + * + * @param [in] x A pointer to a `PARCLinkedList` instance. + * @param [in] y A pointer to a `PARCLinkedList` instance. + * + * @return true `PARCLinkedList` x and y are equal. + * @return false `PARCLinkedList` x and y are not equal. + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool parcLinkedList_Equals(const PARCLinkedList *x, const PARCLinkedList *y); + +/** + * Print a human readable representation of the given `PARCLinkedList`. + * + * @param [in] indentation The level of indentation to use to pretty-print the output. + * @param [in] list A pointer to the instance to display. + * + * Example: + * @code + * { + * PARCLinkedList *instance = parcLinkedList_Create(); + * + * parcLinkedList_Display(instance, 0); + * + * parcLinkedList_Release(&instance); + * } + * @endcode + * + */ +void parcLinkedList_Display(const PARCLinkedList *list, int indentation); + +/** + * Wakes up a single thread that is waiting on this object (see `parcLinkedList_Wait)`. + * If any threads are waiting on this object, one of them is chosen to be awakened. + * The choice is arbitrary and occurs at the discretion of the underlying implementation. + * + * The awakened thread will not be able to proceed until the current thread relinquishes the lock on this object. + * The awakened thread will compete in the usual manner with any other threads that might be actively + * competing to synchronize on this object; + * for example, the awakened thread enjoys no reliable privilege or disadvantage in being the next thread to lock this object. + * + * @param [in] object A pointer to a valid PARCLinkedList instance. + * + * Example: + * @code + * { + * + * parcLinkedList_Notify(object); + * } + * @endcode + */ +parcObject_ImplementNotify(parcLinkedList, PARCLinkedList); + +/** + * Wakes up all threads that are waiting on the given object's lock. + * + * A thread waits on an object by calling one of the wait methods, `parcLinkedList_Wait`, `parcLinkedList_WaitFor`, `parcLinkedList_WaitUntil`. + * The awakened threads will proceed after the current thread relinquishes the lock on the given object. + * The awakened threads will compete in the usual manner with any other threads that might be actively competing + * to synchronize on this object. + * Awakened threads have no priority between them in being the next thread to lock this object. + * + * This method can only be called by a thread that is the owner of this object's lock. + * + * @param [in] object A pointer to a valid `PARCLinkedList` instance. + * + * Example: + * @code + * { + * if (parcLinkedList_Lock(object)) { + * parcLinkedList_NotifyAll(object); + * parcLinkedList_Unlock(object); + * } + * } + * @endcode + */ +parcObject_ImplementNotifyAll(parcLinkedList, PARCLinkedList); + +/** + * Causes the calling thread to wait until either another thread invokes the parcLinkedList_Notify() function on the same object. + * + * @param [in] object A pointer to a valid `PARCLinkedList` instance. + * + * Example: + * @code + * { + * + * parcLinkedList_Wait(object); + * } + * @endcode + */ +parcObject_ImplementWait(parcLinkedList, PARCLinkedList); + +parcObject_ImplementWaitFor(parcLinkedList, PARCLinkedList); + +parcObject_ImplementWaitUntil(parcLinkedList, PARCLinkedList); + +/** + * Obtain the lock on the given `PARCLinkedList` instance. + * + * If the lock is already held by another thread, this function will block. + * If the lock is aleady held by the current thread, this function will return `false`. + * + * Implementors must avoid deadlock by attempting to lock the object a second time within the same calling thread. + * + * @param [in] object A pointer to a valid `PARCLinkedList` instance. + * + * @return true The lock was obtained successfully. + * @return false The lock is already held by the current thread, or the `PARCLinkedList` is invalid. + * + * Example: + * @code + * { + * if (parcLinkedList_Lock(object)) { + * + * } + * } + * @endcode + */ +parcObject_ImplementLock(parcLinkedList, PARCLinkedList); + +/** + * Try to obtain the advisory lock on the given PARCLinkedList instance. + * + * Once the lock is obtained, the caller must release the lock as soon as possible. + * + * @param [in] object A pointer to a valid PARCLinkedList instance. + * + * @return true The PARCLinkedList is locked. + * @return false The PARCLinkedList is unlocked. + * + * Example: + * @code + * { + * parcLinkedList_TryLock(object); + * } + * @endcode + */ +parcObject_ImplementTryLock(parcLinkedList, PARCLinkedList); + +/** + * Try to unlock the advisory lock on the given `PARCLinkedList` instance. + * + * @param [in] object A pointer to a valid `PARCLinkedList` instance. + * + * @return true The `PARCLinkedList` was locked and now is unlocked. + * @return false The `PARCLinkedList` was not locked and remains unlocked. + * + * Example: + * @code + * { + * parcLinkedList_Unlock(object); + * } + * @endcode + */ +parcObject_ImplementUnlock(parcLinkedList, PARCLinkedList); + +/** + * Determine if the advisory lock on the given `PARCLinkedList` instance is locked. + * + * @param [in] object A pointer to a valid `PARCLinkedList` instance. + * + * @return true The `PARCLinkedList` is locked. + * @return false The `PARCLinkedList` is unlocked. + * Example: + * @code + * { + * if (parcLinkedList_IsLocked(object)) { + * ... + * } + * } + * @endcode + */ +parcObject_ImplementIsLocked(parcLinkedList, PARCLinkedList); + +/** + * Determine if two `PARCLinkedList` instances are equivalent sets. + * + * The lists are examined without regard to order. + * If both lists, x and y are of equal length, and all of the elements in list `x` are present in list `y`, this function returns true. + * + * @param [in] x A pointer to a valid `PARCLinkedList` instance. + * @param [in] y A pointer to a valid `PARCLinkedList` instance. + * @return true The instances are equivalent. + * @return false The instances are equivalent. + * + */ +bool parcLinkedList_SetEquals(const PARCLinkedList *x, const PARCLinkedList *y); + +/** + * Insert the given element into the list such that it is the index'th element in the list. + */ +PARCLinkedList *parcLinkedList_InsertAtIndex(PARCLinkedList *list, size_t index, const PARCObject *element); + +/** + * Apply a function to every element in the given PARCLinkedList. + * + * The function is applied in order, any return value is ignored. + * + * @param [in] list A pointer to a valid PARCLinkedList instance. + * @param [in] function A pointer to a function that will be called with each element of the list. + * @param [in] parameter A pointer to arbitrary data that will supplied as an additional parameter to @p function + * + */ +#define parcLinkedList_Apply(_list_, _function_, _parameter_) \ + parcLinkedList_ApplyImpl(_list_, (void (*))_function_, (const void *) _parameter_) +void parcLinkedList_ApplyImpl(PARCLinkedList *list, void (*function)(PARCObject *, const void *), const void *parameter); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in#> | <#out#> | <#in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +PARCList *parcLinkedList_AsPARCList(PARCLinkedList *list); +#endif // libparc_parc_Deque_h diff --git a/libparc/parc/algol/parc_List.c b/libparc/parc/algol/parc_List.c new file mode 100644 index 00000000..0e62cda6 --- /dev/null +++ b/libparc/parc/algol/parc_List.c @@ -0,0 +1,277 @@ +/* + * 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 <LongBow/runtime.h> + +#include <stdio.h> +#include <stdarg.h> + +#include <parc/algol/parc_Object.h> + +#include <parc/algol/parc_List.h> +#include <parc/algol/parc_Memory.h> + +struct parc_list { + void *instance; + const PARCListInterface *interface; +}; + +static void +_destroy(PARCList **listPtr) +{ + PARCList *list = *listPtr; + + (list->interface->Destroy)(&list->instance); +} + +parcObject_ExtendPARCObject(PARCList, _destroy, parcList_Copy, NULL, parcList_Equals, NULL, parcList_HashCode, NULL); + +PARCList * +parcList(void *instance, PARCListInterface *interface) +{ + PARCList *result = parcObject_CreateInstance(PARCList); + if (result != NULL) { + result->instance = instance; + result->interface = interface; + } + + return result; +} + +PARCList * +parcList_Create(PARCObject *instance, PARCListInterface *interface) +{ + PARCList *result = parcObject_CreateInstance(PARCList); + if (result != NULL) { + result->instance = parcObject_Acquire(instance); + result->interface = interface; + } + + return result; +} + +parcObject_ImplementAcquire(parcList, PARCList); + +parcObject_ImplementRelease(parcList, PARCList); + +PARCList * +parcList_Copy(const PARCList *list) +{ + PARCList *result = parcObject_CreateInstance(PARCList); + if (result != NULL) { + result->instance = (list->interface->Copy)(list->instance); + result->interface = list->interface; + } + + return result; +} + +bool +parcList_IsEmpty(const PARCList *list) +{ + bool result = false; + if (list->interface->IsEmpty) { + result = (list->interface->IsEmpty)(list->instance); + } else { + result = (parcList_Size(list) == 0); + } + return result; +} + +bool +parcList_Add(PARCList *list, void *element) +{ + return (list->interface->Add)(list->instance, element); +} + +bool +parcList_AddAll(PARCList *list, size_t argc, void *argv[argc]) +{ + for (int i = 0; i < argc; i++) { + (list->interface->Add)(list->instance, argv[i]); + } + return true; +} + +void +parcList_AddAtIndex(PARCList *list, int index, void *element) +{ + (list->interface->AddAtIndex)(list->instance, index, element); +} + +bool +parcList_AddCollection(PARCList *list, PARCCollection *collection) +{ + return (list->interface->AddCollection)(list->instance, collection); +} + +bool +parcList_AddCollectionAtIndex(PARCList *list, int index, PARCCollection *collection) +{ + return (list->interface->AddCollectionAtIndex)(list->instance, index, collection); +} + +void +parcList_Clear(PARCList *list) +{ + if (!list->interface->Clear) { + for (size_t i = 0; i < parcList_Size(list); i++) { + parcList_RemoveAtIndex(list, i); + } + } else { + (list->interface->Clear)(list->instance); + } +} + +bool +parcList_Contains(const PARCList *list, void *element) +{ + return (list->interface->Contains)(list->instance, element); +} + +bool +parcList_ContainsCollection(PARCList *list, PARCCollection *collection) +{ + return (list->interface->ContainsCollection)(list->instance, collection); +} + +bool +parcList_Equals(const PARCList *x, const PARCList *y) +{ + return (x->interface->Equals)(x->instance, y->instance); +} + +void * +parcList_GetAtIndex(const PARCList *list, size_t index) +{ + return (list->interface->GetAtIndex)(list->instance, index); +} + +int +parcList_HashCode(const PARCList *list) +{ + return (list->interface->HashCode)(list->instance); +} + +ssize_t +parcList_IndexOf(const PARCList *list, PARCObject *element) +{ + ssize_t result = -1; + + if (list->interface->IndexOf) { + result = (list->interface->IndexOf)(list->instance, element); + } else { + for (ssize_t i = 0; i < parcList_Size(list); i++) { + PARCObject *e = parcList_GetAtIndex(list, i); + if (parcObject_Equals(e, element)) { + result = i; + break; + } + } + } + + return result; +} + +ssize_t +parcList_LastIndexOf(const PARCList *list, PARCObject *element) +{ + ssize_t result = -1; + + if (list->interface->LastIndexOf) { + result = (list->interface->LastIndexOf)(list->instance, element); + } else { + for (ssize_t i = parcList_Size(list) - 1; i >= 0; i--) { + PARCObject *e = parcList_GetAtIndex(list, i); + if (parcObject_Equals(e, element)) { + result = i; + break; + } + } + } + + return result; +} + +PARCObject * +parcList_RemoveAtIndex(PARCList *list, size_t index) +{ + if (list->interface->RemoveAtIndex) { + return (list->interface->RemoveAtIndex)(list->instance, index); + } else { + return NULL; + } +} + +bool +parcList_Remove(PARCList *list, PARCObject *element) +{ + bool result = false; + + if (list->interface->Remove != NULL) { + result = (list->interface->Remove)(list->instance, element); + } else { + for (size_t i = 0; i < parcList_Size(list); i++) { + void *e = parcList_GetAtIndex(list, i); + if (parcObject_Equals(e, element)) { + parcList_RemoveAtIndex(list, i); + result = true; + break; + } + } + } + + return result; +} + +bool +parcList_RemoveCollection(PARCList *list, PARCCollection *collection) +{ + return (list->interface->RemoveCollection)(list->instance, collection); +} + +bool +parcList_RetainCollection(PARCList *list, PARCCollection *collection) +{ + return (list->interface->RetainCollection)(list->instance, collection); +} + +PARCObject * +parcList_SetAtIndex(PARCList *list, size_t index, void *element) +{ + return (list->interface->SetAtIndex)(list->instance, index, element); +} + +size_t +parcList_Size(const PARCList *list) +{ + return (list->interface->Size)(list->instance); +} + +PARCList * +parcList_SubList(PARCList *list, size_t fromIndex, size_t toIndex) +{ + return (list->interface->SubList)(list->instance, fromIndex, toIndex); +} + +void** +parcList_ToArray(PARCList *list) +{ + return (list->interface->ToArray)(list->instance); +} diff --git a/libparc/parc/algol/parc_List.h b/libparc/parc/algol/parc_List.h new file mode 100644 index 00000000..6c17453c --- /dev/null +++ b/libparc/parc/algol/parc_List.h @@ -0,0 +1,768 @@ +/* + * 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. + */ + + +/** + * @file parc_List.h + * @ingroup datastructures + * @brief PARC (Generic) List + * + * An ordered collection (also known as a sequence). + * The user of this interface has precise control over where in the list each element is inserted. + * The user can access elements by their integer index (position in the list), and search for elements in the list. + * Unlike sets, lists typically allow duplicate elements. + * More formally, lists typically allow pairs of elements e1 and e2 such that e1.equals(e2), and they typically allow + * multiple null elements if they allow null elements at all. + * It is not inconceivable that someone might wish to implement a list that prohibits duplicates, + * by throwing runtime exceptions when the user attempts to insert them, but we expect this usage to be rare. + * + */ +#ifndef libparc_parc_List_h +#define libparc_parc_List_h + +#include <stdbool.h> + +struct parc_list; +/** + * @typedef PARCList + * @brief An ordered collection (also known as a sequence). + */ +typedef struct parc_list PARCList; + +#include <parc/algol/parc_HashCode.h> + +#include <parc/algol/parc_Collection.h> +#include <parc/algol/parc_Object.h> + +/** + * @typedef PARCListInterface + * @brief The interface of a `PARCList` including functions such as copy, destroy, add, etc. + */ +typedef struct parc_list_interface { + /** + * Copy an instance of `PARCList` + * + * @param [in] original An instance of `PARCList` to copy + * + * @return A pointer to the new list. + */ + void *(*Copy)(const PARCList * original); + + /** + * Destroy the List + * + * @param [in,out] instancePtr + * @return a pointer to the destroyed List. + */ + void (*Destroy)(void **instancePtr); + + /** + * Tests if this list is empty. + * + * @param [in] instance + * @return true if the list is empty + */ + bool (*IsEmpty)(const void *instance); + + /** + * Appends the specified element to the end of this list (optional operation). + * + * @param [in,out] The instance of `PARCList` to append the element to + * @param [in] element The pointer to the element to be added to the `PARCList` + * @return true if the element was added successfully. + */ + bool (*Add)(void *instance, PARCObject *element); + + /** + * Inserts the specified element at the specified position in this list (optional operation). + * + * @param [in,out] instance The instance of `PARCList` to modify + * @param [in] index The index in `PARCList` at which to insert the @p element + * @param [in] element The element to insert in `PARCList` at @p index. + */ + void (*AddAtIndex)(void *instance, int index, PARCObject *element); + + /** + * Append elements of @p collection to @p instance + * + * Appends all of the elements in the specified collection to the end of this list, + * in the order that they are returned by the specified collection's iterator (optional operation). + * + * @param [in,out] instance The `PARCList` to be modified + * @param [in] collection The collection to be added + * @return true if add is successful. + */ + bool (*AddCollection)(void *instance, PARCCollection *collection); + + /** + * Inserts all of the elements in the specified collection into this list at the specified position (optional operation) + * + * @param [in,out] instance The `PARCList` to be modified + * @param [in] index The position at which to insert the @p collection + * @param [in] collection The collection to be added + * @return true if add is successful. + * @endcode + */ + bool (*AddCollectionAtIndex)(void *instance, int index, PARCCollection *collection); + + /** + * Removes all of the elements from this list (optional operation). + * + * @param [in,out] instance The instance of `PARCList` to empty. + */ + void (*Clear)(void *instance); + + /** + * Returns true if this list contains the specified element. + * + * @param [in] instance The instance of `PARCList` to inspect + * @param [in] element The element to search for in @p instance + * @return true if the @p element is found in @p instance. + */ + bool (*Contains)(const void *instance, const PARCObject *element); + + /** + * Returns true if this list contains all of the elements of the specified collection. + * + * @param [in] instance The instance of `PARCList` to inspect + * @param [in] collection The instance of {@link PARCCollection} whose elements are sought in @p instance + * @return true if all of the elements in @p collection is found in @p instance + */ + bool (*ContainsCollection)(const void *instance, const PARCCollection *collection); + + /** + * Compares the specified object with this list for equality. + * + * @param [in] xInstance The first `PARCList` instance to compare + * @param [in] yInstance The second `PARCList` instance to compare + * @return true if the two instances are equal + */ + bool (*Equals)(const void *xInstance, const void *yInstance); + + /** + * Returns the element at the specified position in this list. + * + * @param [in] instance A pointer to the instance of `PARCList` + * @param index The index of the element to be returned + * @return A pointer to the element at @p index + */ + PARCObject *(*GetAtIndex)(const void *instance, size_t index); + + /** + * Returns the hash code value for this list. + * + * @param [in] instance A pointer to the instance of `PARCList` + * @return int The hash code value + */ + PARCHashCode (*HashCode)(const void *instance); + + /** + * Returns the index of the first occurrence of the specified element in this list, or -1 if this list does not contain the element. + * + * @param [in] instance A pointer to the instance of `PARCList` + * @param [in] element A pointer to the element to locate in @p instance + * @return size_t the index of the first located @p element or -1 if not found + */ + size_t (*IndexOf)(const void *instance, const PARCObject *element); + + /** + * Returns the index of the last occurrence of the specified element in this list, or -1 if this list does not contain the element. + * + * @param [in] instance A pointer to the instance of `PARCList` + * @param [in] element A pointer to the element to locate in @p instance + * @return size_t the index of the last located @p element or -1 if not found + * + * Example: + * @code + * <#example#> + * @endcode + */ + size_t (*LastIndexOf)(void *instance, const PARCObject *element); + + /** + * Removes the element at the specified position in this list (optional operation). + * + * @param [in,out] instance A pointer to the instance of `PARCList` to modify + * @param [in] index The index of the element to remove + * @return A pointer to the removed element + * + * Example: + * @code + * <#example#> + * @endcode + */ + PARCObject *(*RemoveAtIndex)(PARCList * list, size_t index); + + /** + * Removes the first occurrence of the specified element from this list, if it is present (optional operation). + * + * @param [in,out] instance A pointer to the instance of `PARCList` to modify + * @param element The element to find and remove + * @return true if element found and removed + * + * Example: + * @code + * <#example#> + * @endcode + */ + bool (*Remove)(void *instance, const PARCObject *element); + + /** + * Removes from this list all of its elements that are contained in the specified collection (optional operation). + * + * @param [in,out] instance A pointer to the instance of `PARCList` to modify + * @param collection The instance of {@link PARCCollection} whose elements should be found in the @p instance and removed. + * @return true if the elements are found and removed + * + * Example: + * @code + * <#example#> + * @endcode + */ + bool (*RemoveCollection)(void *instance, const PARCCollection *collection); + + /** + * Retains only the elements in this list that are contained in the specified collection (optional operation). + * + * @param [in,out] instance A pointer to the instance of `PARCList` to modify + * @param collection The instance of {@link PARCCollection} whose elements should be retained in + * the @p instance while all other elements are removed. + * @return true if the operation is successful + * + * Example: + * @code + * <#example#> + * @endcode + */ + bool (*RetainCollection)(void *instance, const PARCCollection *collection); + + /** + * Replaces the element at the specified position in this list with the specified element (optional operation). + * + * @param [in,out] instance A pointer to the instance of `PARCList` to modify + * @param index The position in @p instance to replace with @p element + * @param element The element to put into @p instance at @p index, replacing the current value. + * @return + * + * Example: + * @code + * <#example#> + * @endcode + */ + void *(*SetAtIndex)(void *instance, size_t index, PARCObject * element); + + /** + * Returns the number of elements in this list. + * + * @param [in] instance A pointer to the instance of `PARCList` to inspect + * @return size_t Number of elements in the list + * + * Example: + * @code + * <#example#> + * @endcode + */ + size_t (*Size)(const void *instance); + + /** + * Returns a view of the portion of this list between the specified fromIndex, inclusive, and toIndex, exclusive. + * + * @param [in] instance A pointer to the instance of `PARCList` to inspect + * @param [in] fromIndex The starting index into the list + * @param [in] toIndex The end index into the list + * @return A pointer to the sub list + * + * Example: + * @code + * <#example#> + * @endcode + */ + PARCList *(*SubList)(const void *instance, size_t fromIndex, size_t toIndex); + + /** + * Returns an array containing all of the elements in this list in proper sequence (from first to last element). + * + * @param [in] instance A pointer to the instance of `PARCList` to inspect + * @return A pointer to a pointer to the array containing the elements of the list in proper sequence. + * + * Example: + * @code + * <#example#> + * @endcode + */ + void** (*ToArray)(const void *instance); +} PARCListInterface; + +/** + * Increase the number of references to a `PARCList`. + * + * Note that new `PARCList` is not created, + * only that the given `PARCList` reference count is incremented. + * Discard the reference by invoking `parcList_Release`. + * + * @param list A pointer to the original `PARCList`. + * @return The value of the input parameter @p list. + * + * Example: + * @code + * { + * PARCList *list = parcList(parcArrayList_Create(parcArrayList_StdlibFreeFunction), PARCArrayListAsPARCList); + * + * PARCList *list2 = parcList_Acquire(list); + * + * parcList_Release(&list); + * parcList_Release(&list2); + * } + * @endcode + * + * @see parcList_Release + */ +PARCList *parcList_Acquire(const PARCList *list); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's interface will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] listPtr A pointer to a pointer to the instance to release. + * + * Example: + * @code + * { + * PARCList *list = parcList(parcArrayList_Create(parcArrayList_StdlibFreeFunction), PARCArrayListAsPARCList); + * + * parcList_Release(&list); + * } + * @endcode + */ +void parcList_Release(PARCList **listPtr); + +/** + * Create an independent copy the given `PARCList` + * + * A new list is created as a complete copy of the original. + * + * @param [in] list A valid pointer to a `PARCList` instance (cannot be NULL) + * + * @return NULL Memory could not be allocated. + * @return non-NULL A pointer to a new `PARCList` instance. + * + * @throws trapIllegalValue if @p instance is NULL. + * + * Example: + * @code + * { + * PARCList *list = parcList(parcArrayList_Create(parcArrayList_StdlibFreeFunction), PARCArrayListAsPARCList); + * + * PARCList *copy = parcList_Copy(list); + * + * parcList_Release(©); + * parcList_Release(&list); + * } + * @endcode + * + */ +PARCList *parcList_Copy(const PARCList *list); + +/** + * Tests if this list is empty. + * + * Return true if the list is empty, false otherwise + * + * @param list A pointer to the instance of `PARCList` to test + * @return True if the list is empty, else False + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool parcList_IsEmpty(const PARCList *list); + +/** + * Appends the specified element to the end of this list (optional operation). + * + * @param [in,out] list A pointer to the instance of `PARCList` to modify + * @param [in] element The element to add to the end of the `PARCList` + * @return True if the add is successful + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool parcList_Add(PARCList *list, void *element); + +/** + * Add all of the pointers in the given array of pointers to the `PARCList`. + * + * @param [in,out] list A pointer to the `PARCList` instance to be modified. + * @param [in] argc The number of values in @p argv. + * @param [in] argv An array void * values. + * + * @return True if the add is successful + * + * Example: + * @code + * { + * PARCList *list = parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList); + * + * int elements[] = { 1, 2, 3 }; + * + * parcList_AddAll(array, 3, elements); + * size_t actual = parcList_Length(array); + * + * assertTrue(3 == actual, "Expected=%d, actual=%d", 3, actual); + * + * parcListRelease(&array); + * } + * @endcode + */ +bool parcList_AddAll(PARCList *list, size_t argc, void **argv); + +/** + * Inserts the specified element at the specified position in this list (optional operation). + * + * @param [in,out] list A pointer to the `PARCList` instance to be modified + * @param [in] index The specified position in the list + * @param [in] element The element to be added to the specified position. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void parcList_AddAtIndex(PARCList *list, int index, void *element); + +/** + * Appends all of the elements in the specified collection to the end of this list, + * in the order that they are returned by the specified collection's iterator (optional operation). + * + * @param [in,out] list A pointer to the `PARCList` instance to be modified + * @param [in] collection A pointer to an istance of {@link PARCCollection} to be added to the list + * @return True if add is successful + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool parcList_AddCollection(PARCList *list, PARCCollection *collection); + +/** + * Inserts all of the elements in the specified collection into this list at the specified position (optional operation). + * + * @param [in,out] list A pointer to the `PARCList` instance to be modified + * @param [in] index The position at which to insert the collection + * @param [in] collection A pointer to an istance of {@link PARCCollection} to be inserted into the list + * @return True if insertion is successful + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool parcList_AddCollectionAtIndex(PARCList *list, int index, PARCCollection *collection); + +/** + * Removes all of the elements from this list (optional operation). + * + * @param [in,out] list A pointer to the `PARCList` instance to be cleared + * + * Example: + * @code + * <#example#> + * @endcode + */ +void parcList_Clear(PARCList *list); + +/** + * Returns true if this list contains the specified element. + * + * @param [in] list A pointer to the `PARCList` instance to be checked + * @param [in] element The element to be added to the specified position. + * @return True if the element is contained in the list + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool parcList_Contains(const PARCList *list, void *element); + +/** + * Returns true if this list contains all of the elements of the specified collection. + * + * @param [in] list A pointer to the `PARCList` instance to be checked + * @param [in] collection A pointer to the instance of {@link PARCCollection} to be checked. + * @return True if all of the elements in the collection are found in the list. + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool parcList_ContainsCollection(PARCList *list, PARCCollection *collection); + + +/** + * Determine if two `PARCList` instances are equal. + * + * Two `PARCList` instances are equal if, and only if, + * the size of the lists are equal and each element in the list is equal and + * in the same order. + * + * The following equivalence relations on non-null `PARCList` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, `PARCList_Equals(x, x)` + * must return true. + * + * * It is symmetric: for any non-null reference values x and y, + * `parcList_Equals(x, y)` must return true if and only if + * `parcList_Equals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcList_Equals(x, y)` returns true and + * `parcList_Equals(y, z)` returns true, + * then `parcList_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple + * invocations of `parcList_Equals(x, y)` consistently return true or + * consistently return false. + * + * * For any non-null reference value x, `parcList_Equals(x, NULL)` must + * return false. + * + * @param [in] x A pointer to a `PARCList` instance. + * @param [in] y A pointer to a `PARCList` instance. + * @return true if the two `PARCList` instances are equal. + * + * Example: + * @code + * { + * PARCList *a = parcList_Create(); + * PARCList *b = parcList_Create(); + * + * if (parcList_Equals(a, b)) { + * // true + * } else { + * // false + * } + * } + * @endcode + */ +bool parcList_Equals(const PARCList *x, const PARCList *y); + +/** + * Returns the element at the specified position in this list. + * If the index is out of bounds, it will trap with an out-of-bounds. + * + * @param [in] list A pointer to the `PARCList` instance to be checked + * @param [in] index The index of the element to be returned + * @return A pointer to the element. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void *parcList_GetAtIndex(const PARCList *list, size_t index); + +/** + * Returns the hash code value for this list. + * + * @param [in] list A pointer to the `PARCList` instance to be hashed + * @return The hash code value + * + * Example: + * @code + * <#example#> + * @endcode + */ +int parcList_HashCode(const PARCList *list); + +/** + * Returns the index of the first occurrence of the specified element in this list, or -1 if this list does not contain the element. + * + * @param [in] list A pointer to the `PARCList` instance to be hashed. + * @param [in] element A pointer to an element to check for list inclusion. + * @return The index of the first occurance of @p element, or -1 if it is not present + * Example: + * @code + * <#example#> + * @endcode + */ +ssize_t parcList_IndexOf(const PARCList *list, PARCObject *element); + +/** + * Returns the index of the last occurrence of the specified element in this list, or -1 if this list does not contain the element. + * + * @param [in] list A pointer to the `PARCList` instance to be hashed. + * @param [in] element A pointer to an element to check for list inclusion. + * @return The index of the last occurance of @p element, or -1 if it is not present + * + * Example: + * @code + * <#example#> + * @endcode + */ +ssize_t parcList_LastIndexOf(const PARCList *list, PARCObject *element); + +/** + * Removes the element at the specified position in this list (optional operation). + * + * @param [in,out] list A pointer to the `PARCList` instance to be modified. + * @param [in] index The index of the element to be removed + * @return non NULL A pointer to the element removed + * + * Example: + * @code + * <#example#> + * @endcode + */ +void *parcList_RemoveAtIndex(PARCList *list, size_t index); + +/** + * Removes the first occurrence of the specified element from this list, if it is present (optional operation). + * + * @param [in,out] list A pointer to the `PARCList` instance to be modified. + * @param [in] element A pointer to the element to be removed from the `PARCList` + * @return true The element was found and removed, false if it was not found. + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool parcList_Remove(PARCList *list, PARCObject *element); + +/** + * Removes from this list all of its elements that are contained in the specified collection (optional operation). + * + * @param [in,out] list A pointer to the `PARCList` instance to be modified. + * @param [in] collection A pointer to the instance of {@link PARCCollection} to be removed from the `PARCList` + * @return true The collection was found and removed, false if it was not found. + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool parcList_RemoveCollection(PARCList *list, PARCCollection *collection); + +/** + * Retains only the elements in this list that are contained in the specified collection (optional operation). + * + * @param [in,out] list A pointer to the `PARCList` instance to be modified. + * @param [in] collection A pointer to the instance of {@link PARCCollection} to be found and retained in the `PARCList` + * @return true if the function was successful + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool parcList_RetainCollection(PARCList *list, PARCCollection *collection); + +/** + * Replaces the element at the specified position in this list with the specified element (optional operation). + * + * @param [in,out] list A pointer to the `PARCList` instance to be modified. + * @param [in] index The position at which the element should be replaced with @p element + * @param [in] element A pointer to the element to be inserted at the specified position + * @return A pointer to the element previously at that position. + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCObject *parcList_SetAtIndex(PARCList *list, size_t index, PARCObject *element); + +/** + * Returns the number of elements in this list. + * + * @param [in] list A pointer to the `PARCList` instance to be measured. + * @return The size of the @p list + * + * Example: + * @code + * <#example#> + * @endcode + */ +size_t parcList_Size(const PARCList *list); + +/** + * Returns a view of the portion of this list between the specified fromIndex, inclusive, and toIndex, exclusive. + * + * @param [in] list A pointer to the `PARCList` instance to be measured. + * @param [in] fromIndex The position to start the view (inclusive) + * @param [in] toIndex The position to end the view (exclusive) + * @return a pointer to an instance of `PARCList` containing the subList requested + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCList *parcList_SubList(PARCList *list, size_t fromIndex, size_t toIndex); + +/** + * Returns an array containing all of the elements in this list in proper sequence (from first to last element). + * + * @param [in] list A pointer to the `PARCList` instance to be sorted. + * @return A pointer to a pointer to an Array containing the sorted elements + * + * Example: + * @code + * <#example#> + * @endcode + */ +void**parcList_ToArray(PARCList *list); + +/** + * Create an instance of `PARCList` that uses the @p interface to provide functions and the @p instance to provide + * initial elements of the list. + * @param [in] instance An initial set of elements for the new instance of `PARCList` + * @param [in] interface A pointer to an instance of {@link PARCListInterface} containing a set of list functions + * @return A pointer to a new instance of `PARCList` + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCList *parcList(void *instance, PARCListInterface *interface); + +/** + * Create an instance of `PARCList` that uses the @p interface to provide functions and the @p instance to provide + * initial elements of the list. + * @param [in] instance An initial set of elements for the new instance of `PARCList` + * @param [in] interface A pointer to an instance of {@link PARCListInterface} containing a set of list functions + * @return A pointer to a new instance of `PARCList` + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCList *parcList_Create(void *instance, PARCListInterface *interface); + +#endif // libparc_parc_List_h diff --git a/libparc/parc/algol/parc_Map.c b/libparc/parc/algol/parc_Map.c new file mode 100755 index 00000000..e5371c99 --- /dev/null +++ b/libparc/parc/algol/parc_Map.c @@ -0,0 +1,20 @@ +/* + * 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 <parc/algol/parc_Map.h> diff --git a/libparc/parc/algol/parc_Map.h b/libparc/parc/algol/parc_Map.h new file mode 100755 index 00000000..c63b23c5 --- /dev/null +++ b/libparc/parc/algol/parc_Map.h @@ -0,0 +1,454 @@ +/* + * 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. + */ + +/** + * @file parc_Map.h + * @ingroup datastructures + * @brief An object that maps keys to values. + * + * A map cannot contain duplicate keys; each key can map to at most one value. + * + */ +#ifndef libparc_parc_Map_h +#define libparc_parc_Map_h +#include <stdbool.h> + +struct parc_map; +typedef struct parc_map PARCMap; + +typedef struct parc_map_interface { + /** + * Removes all of the mappings from this map. + * + * The map will be empty after this call returns. + * + * @param [in,out] map The instance of `PARCMap` to be cleared of mappings + * + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + * + */ + void (*parcMap_Clear)(PARCMap *map); + + /** + * Returns true if this map contains a mapping for the specified key. + * + * @param [in] map A pointer to the instance of `PARCMap` to check + * @param [in] key A pointer to the key to check for in @p map + * + * @return True if the map cnatins a mapping for the specified key + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ + bool (*parcMap_ContainsKey)(PARCMap *map, void *key); + + /** + * Returns true if this map maps one or more keys to the specified value. + * + * @param [in] map A pointer to the instance of `PARCMap` to check + * @param [in] value A pointer to the value to check for in @p map + * + * @return True if the map contains one or more keys that map to @p value. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ + bool (*parcMap_ContainsValue)(PARCMap *map, void *value); + + /** + * Compares the specified object with this map for equality. + * + * @param [in] map A pointer to the instance of `PARCMap` to check + * @param [in] other A pointer to the other instance of `PARCMap` to compare + * @return True is the two maps are equal. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + * + */ + bool (*parcMap_Equals)(PARCMap *map, void *other); + + /** + * Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key. + * + * @param [in] map A pointer to the instance of `PARCMap` to check + * @param [in] key A pointer to the key to check for in @p map + * + * @return NULL If the @p key is not present in @p map + * @return NOT NULL The value to which the @p key is mapped. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + * + */ + void *(*parcMap_Get)(PARCMap * map, void *key); + + /** + * Returns the hash code value for this map. + * + * @param [in] map A pointer to the instance of `PARCMap` to hash + * + * @return The hash of the instance of `PARCMap` + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ + int (*parcMap_HashCode)(PARCMap *map); + + /** + * Returns true if this map contains no key-value mappings. + * + * + * @param [in] map A pointer to the instance of `PARCMap` to check + * + * @return True if the map contains no mappings. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ + bool (*parcMap_IsEmpty)(PARCMap *map); + + /** + * Associates the specified value with the specified key in this map (optional operation). + * + * @param [in,out] map A pointer to the instance of `PARCMap` in which to insert @p value at @p key. + * @param [in] key A pointer to the key in @p map in which to insert @p value. + * @param [in] value A pointer to the the value to insert at @p key in @p map. + * + * @return + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ + void *(*parcMap_Put)(PARCMap * map, void *key, void *value); + + /** + * Copies all of the mappings from the specified map to this map (optional operation). + * + * @param [in,out] map The instance of `PARCMap` to be modified. + * @param [in] other The instance of `PARCMap` whose mappings should be copied to @p map. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ + void (*parcMap_PutAll)(PARCMap *map, PARCMap *other); + + /** + * Removes the mapping for a key from this map if it is present (optional operation). + * + * @param [in,out] map The instance of `PARCMap` to be modified. + * @param [in] key The key to the mapping to be removed + * + * @return + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ + void *(*parcMap_Remove)(PARCMap * map, void *key); + + /** + * Returns the number of key-value mappings in this map. + * + * @param [in,out] map The instance of `PARCMap` to be inspected. + * + * @return int The number of mappings in the map + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + * + */ + int (*parcMap_Size)(PARCMap *map); +} PARCMapInterface; + +/** + * Create a PARCMap instance. + * + * Create an instance of `PARCMap` wrapping the given pointer to a base map + * interface and the {@ link PARCMapInterface} structure containing pointers + * to functions performing the actual Map operations. + * + * @param [in] map A pointer to the structure for the new instance of `PARCMap` + * @param [in] interface A pointer to the instance of `PARCMapInterface` + * @return A new instance of `PARCMap` + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +PARCMap *parcMap_Create(void *map, PARCMapInterface *interface); + +/** + * Removes all of the mappings from this map. + * + * The map will be empty after this call returns. + * + * @param [in,out] map A pointer to the instance of `PARCMap` to be cleared. + * + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +void parcMap_Clear(PARCMap *map); + +/** + * Returns true if this map contains a mapping for the specified key. + * + * @param [in] map A pointer to the instance of `PARCMap` to be checked. + * @param [in] key A pointer to the key to be checked for in @p map + * + * @return True if the specified key is found in the map. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +bool parcMap_ContainsKey(PARCMap *map, void *key); + +/** + * Returns true if this map maps one or more keys to the specified value. + * + * @param [in] map A pointer to the instance of `PARCMap` to be checked. + * @param [in] value A pointer to the value to be checked for in @p map + * + * @return True if the specified value has one or more keys pointing to it. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +bool parcMap_ContainsValue(PARCMap *map, void *value); + +/** + * Determine if two `PARCMap` instances are equal. + * + * Two `PARCMap` instances are equal if, and only if, the maps have the same + * number of elements, all of the keys are equal and the values to which they point are equal + * + * The following equivalence relations on non-null `PARCMap` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, `PARCMap_Equals(x, x)` + * must return true. + * + * * It is symmetric: for any non-null reference values x and y, + * `parcMap_Equals(x, y)` must return true if and only if + * `parcMap_Equals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcMap_Equals(x, y)` returns true and + * `parcMap_Equals(y, z)` returns true, + * then `parcMap_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple + * invocations of `parcMap_Equals(x, y)` consistently return true or + * consistently return false. + * + * * For any non-null reference value x, `parcMap_Equals(x, NULL)` must + * return false. + * + * @param [in] map A pointer to a `PARCMap` instance. + * @param [in] other A pointer to a `PARCMap` instance. + * @return true if the two `PARCMap` instances are equal. + * + * Example: + * @code + * { + * PARCMap *a = parcMap_Create(); + * PARCMap *b = parcMap_Create(); + * + * if (parcMap_Equals(a, b)) { + * // true + * } else { + * // false + * } + * } + * @endcode + */ +bool parcMap_Equals(PARCMap *map, void *other); + +/** + * Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key. + * + * @param [in] map A pointer to the instance of `PARCMap` to be checked. + * @param [in] key A pointer to the key to be checked for which the value is to be returned. + * + * @return Null if no mapping for @p key exists + * @return Non Null A pointer to the value for @p key + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +void *parcMap_Get(PARCMap *map, void *key); + +/** + * Returns the hash code value for this map. + * + * @param [in] map A pointer to the instance of `PARCMap` to be hashed. + * + * @return The hash value for the @p map + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +int parcMap_HashCode(PARCMap *map); + +/** + * Returns true if this map contains no key-value mappings. + * + * @param [in] map A pointer to the instance of `PARCMap` to be checked. + * + * @return True if the @p map is empty. else false. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +bool parcMap_IsEmpty(PARCMap *map); + +/** + * Associates the specified value with the specified key in this map (optional operation). + * + * @param [in,out] map A pointer to the instance of `PARCMap` into which the key,value pair should be inserted. + * @param [in] key A pointer to the key to be inserted in @p map + * @param [in] value A pointer to the value to be inserted in @p map at @p key + * + * @return The previous value at @p key if one exists, else NULL + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +void *parcMap_Put(PARCMap *map, void *key, void *value); + +/** + * Copies all of the mappings from the specified map to this map (optional operation). + * + * @param [in,out] map The map into which all the mappings from @p other should be copied. + * @param [in] other The instance of `PARCMap` whose mappings should be copied into @p map + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +void parcMap_PutAll(PARCMap *map, PARCMap *other); + +/** + * Removes the mapping for a key from this map if it is present (optional operation). + * + * + * @param [in,out] map The instance of `PARCMap` in which @p key should be removed if present. + * @param [in] key The pointer to the key representing the mapping that should be removed from @p map. + * + * @return A pointer to the value previously mapped to @p key, if @p key exists. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +void *parcMap_Remove(PARCMap *map, void *key); + +/** + * Returns the number of key-value mappings in this map. + * + * @param [in,out] map The instance of `PARCMap` to be measured + * + * @return The number of mappings in @p map. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +int parcMap_Size(PARCMap *map); +#endif // libparc_parc_Map_h diff --git a/libparc/parc/algol/parc_Memory.c b/libparc/parc/algol/parc_Memory.c new file mode 100755 index 00000000..e444baba --- /dev/null +++ b/libparc/parc/algol/parc_Memory.c @@ -0,0 +1,144 @@ +/* + * 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 <LongBow/runtime.h> + +#include <unistd.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <stdio.h> + +#include <stdint.h> +#include <stdbool.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_SafeMemory.h> +#include <parc/algol/parc_StdlibMemory.h> + +static const PARCMemoryInterface *parcMemory = &PARCStdlibMemoryAsPARCMemory; + +const PARCMemoryInterface * +parcMemory_SetInterface(const PARCMemoryInterface *memoryProvider) +{ + assertFalse(memoryProvider == &PARCMemoryAsPARCMemory, + "You cannot use PARCMemoryAsPARCMemory as a memory provider for parcMemory."); + const PARCMemoryInterface *result = parcMemory; + parcMemory = memoryProvider; + + return result; +} + +size_t +parcMemory_RoundUpToCacheLine(const size_t size) +{ + return parcMemory_RoundUpToMultiple(size, LEVEL1_DCACHE_LINESIZE); +} + +size_t +parcMemory_RoundUpToMultiple(const size_t size, const size_t multiple) +{ + if (size == 0) { + return multiple; + } + + if (multiple == 0) { + return size; + } + + size_t remainder = size % multiple; + if (remainder == 0) { + return size; + } + return size + multiple - remainder; +} + +void * +parcMemory_Allocate(const size_t size) +{ + return ((PARCMemoryAllocate *) parcMemory->Allocate)(size); +} + +void * +parcMemory_AllocateAndClear(const size_t size) +{ + return ((PARCMemoryAllocateAndClear *) parcMemory->AllocateAndClear)(size); +} + +int +parcMemory_MemAlign(void **pointer, const size_t alignment, const size_t size) +{ + return ((PARCMemoryMemAlign *) parcMemory->MemAlign)(pointer, alignment, size); +} + +void +parcMemory_DeallocateImpl(void **pointer) +{ + ((PARCMemoryDeallocate *) parcMemory->Deallocate)(pointer); +} + +void * +parcMemory_Reallocate(void *pointer, size_t newSize) +{ + return ((PARCMemoryReallocate *) parcMemory->Reallocate)(pointer, newSize); +} + +char * +parcMemory_StringDuplicate(const char *string, const size_t length) +{ + return ((PARCMemoryStringDuplicate *) parcMemory->StringDuplicate)(string, length); +} + +uint32_t +parcMemory_Outstanding(void) +{ + return ((PARCMemoryOutstanding *) parcMemory->Outstanding)(); +} + +char * +parcMemory_Format(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + + va_list copy; + va_copy(copy, ap); + int length = vsnprintf(NULL, 0, format, copy); + va_end(copy); + + char *result = NULL; + if (length >= 0) { + result = parcMemory_Allocate(length + 1); + + if (result != NULL) { + vsprintf(result, format, ap); + } + } + return result; +} + +PARCMemoryInterface PARCMemoryAsPARCMemory = { + .Allocate = (uintptr_t) parcMemory_Allocate, + .AllocateAndClear = (uintptr_t) parcMemory_AllocateAndClear, + .MemAlign = (uintptr_t) parcMemory_MemAlign, + .Deallocate = (uintptr_t) parcMemory_DeallocateImpl, + .Reallocate = (uintptr_t) parcMemory_Reallocate, + .StringDuplicate = (uintptr_t) parcMemory_StringDuplicate, + .Outstanding = (uintptr_t) parcMemory_Outstanding +}; diff --git a/libparc/parc/algol/parc_Memory.h b/libparc/parc/algol/parc_Memory.h new file mode 100644 index 00000000..fe3c4b5a --- /dev/null +++ b/libparc/parc/algol/parc_Memory.h @@ -0,0 +1,468 @@ +/* + * 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. + */ + +/** + * @file parc_Memory.h + * @ingroup memory + * @brief A Facade to memory allocation features. + * + * PARC Memory provides an interface implementing many regularly available memory allocation functions. + * This interface is a Facade that software implementors may use to substitute different kinds of underlying + * Interfaces of these allocation fucntions. + * Notable examples are PARC Safe Memory and PARC Stdlib Memory. + * + */ +#ifndef libparc_parc_Memory_h +#define libparc_parc_Memory_h + +#include <stdlib.h> +#include <stdint.h> + +/** + * @typedef PARCMemoryAllocate + * @brief Function signature for memory allocator. + * + */ +typedef void *(PARCMemoryAllocate)(size_t size); + +typedef void *(PARCMemoryAllocateAndClear)(size_t size); + +typedef int (PARCMemoryMemAlign)(void **pointer, size_t alignment, size_t size); + +typedef void (PARCMemoryDeallocate)(void **pointer); + +typedef void *(PARCMemoryReallocate)(void *pointer, size_t newSize); + +typedef char *(PARCMemoryStringDuplicate)(const char *string, size_t length); + +typedef uint32_t (PARCMemoryOutstanding)(void); + +/** + * @typedef PARCMemoryInterface + * @brief A structure containing pointers to functions that implement a PARC Memory manager. + * + * A 'PARC Memory' manager is a collection of inter-dependant functions that perform memory allocation, + * re-allocation, deallocation, and housekeeping. + * + * PARC Memory managers are cascadable, where one Interface may call other Interface in a chain. + * This permits the design and Interface of PARC Memory managers that specialise in fixed length memory sizes, + * reference counting, debugging and so forth. + */ +typedef struct parc_memory_interface { + /** + * A pointer to a function that allocates @p size bytes of memory + * and returns the allocation in the return value. + * + * @param [in] size The number of bytes to allocate. + * + * @return A `void *` pointer indicating the address of the allocated memory. + * @return NULL Memory allocation error. + * + * @see AllocateAndClear + * @see Reallocate + */ + uintptr_t Allocate; + + /** + * Performs the same operation as `Allocate` and then sets each byte of the allocated memory to zero. + * + * @param [in] size The number of bytes to allocate. + * + * @return A `void *` pointer indicating the address of the allocated memory. + * @return NULL Memory allocation error. + * + * @see Allocate + * @see Reallocate + */ + uintptr_t AllocateAndClear; + + /** + * A pointer to a function that allocates @p size bytes of memory such that the allocation's + * base address is an exact multiple of alignment, + * and returns the allocation in the value pointed to by @p pointer. + * + * The requested alignment must be a power of 2 greater than or equal to `sizeof(void *)`. + * + * Memory that is allocated can be used as an argument in subsequent call `Reallocate`, however + * `Reallocate` is not guaranteed to preserve the original alignment. + * + * @param [out] pointer A pointer to a `void *` pointer that will be set to the address of the allocated memory. + * @param [in] alignment A power of 2 greater than or equal to `sizeof(void *)` + * @param [in] size The number of bytes to allocate. + * + * @return 0 Successful + * @return EINVAL The alignment parameter is not a power of 2 at least as large as sizeof(void *) + * @return ENOMEM Memory allocation error. + * + * @see posix_memalign + */ + uintptr_t MemAlign; + + /** + * Deallocate memory previously allocated via `Allocate` or `AllocateAndClear`. + * + * @param [in,out] pointer A pointer to a `void *` pointer to the address of the allocated memory that will be set to zero. + * + * @see AllocateAndClear + * @see Allocate + * @see Reallocate + */ + uintptr_t Deallocate; + + /** + * Try to change the size of the allocation pointed to by @p pointer to @p newSize, and returns ptr. + * If there is not enough room to enlarge the memory allocation pointed to by @p pointer, + * create a new allocation, + * copy as much of the old data pointed to by @p pointer as will fit to the new allocation, + * deallocate the old allocation, + * and return a pointer to the allocated memory. + * + * If @p pointer is `NULL`, + * simply invoke the `Allocate` function to allocate memory aligned to the value of `sizeof(void *)` of @p newSize bytes. + * If @p newSize is zero and @p pointer is not NULL, + * a new, minimum sized object is allocated and the original object is freed. + * + * When extending a region previously allocated with `AllocateAndClear`, + * the additional memory is not guaranteed to be zero-filled. + * + * @param [in] pointer A pointer to previously allocated memory, or NULL. + * @param [in] newSize The size of the allocated memory. + * + * @return non-NULL A pointer to allocated memory. + * @return NULL A an error occurred. + * + * @see Deallocate + * @see AllocateAndClear + * @see Allocate + */ + uintptr_t Reallocate; + + /** + * Allocate sufficient memory for a copy of the string @p string, + * copy at most n characters from the string @p string into the allocated memory, + * and return the pointer to allocated memory. + * + * The copied string is always null-terminated. + * + * @param [in] string A pointer to a null-terminated string. + * @param [length] The maximum allows length of the resulting copy. + * + * @return non-NULL A pointer to allocated memory. + * @return NULL A an error occurred. + */ + uintptr_t StringDuplicate; + + /** + * Return the number of allocations outstanding. That is, the numbe of allocations + * that have been made, but not yet freed. + * + * @return The number of outstanding allocations known to this `PARCMemoryInterface`. + */ + uintptr_t Outstanding; +} PARCMemoryInterface; + +/** + * + */ +extern PARCMemoryInterface PARCMemoryAsPARCMemory; + +/** + * Set the current memory allocation interface. + * + * The previous interface is returned. + * + * @param [in] memoryProvider A pointer to a {@link PARCMemoryInterface} instance. + * + * @return A pointer to the previous `PARCMemoryInterface` instance. + * + * Example: + * @code + * { + * PARCMemoryInterface *previousInterface = parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + * } + * @endcode + * + * @see PARCSafeMemoryAsPARCMemory + * @see PARCMemoryAsPARCMemory + * @see PARCStdlibMemoryAsPARCMemory + */ +const PARCMemoryInterface *parcMemory_SetInterface(const PARCMemoryInterface *memoryProvider); + +/** + * Allocate memory. + * + * Allocates @p size bytes of memory and returns the allocation in the return value. + * + * Memory that is allocated can be used as an argument in subsequent call `Reallocate`. + * + * @param [in] size The number of bytes to allocate. + * + * @return A `void *` pointer set to the address of the allocated memory. + * @return NULL Memory allocation error. + * + * Example: + * @code + * { + * void *allocatedMemory; + * + * allocateMemory = parcMemory_Allocate(100); + * if (allocatedMemory == NULL) { + * // allocation failed. + * } + * } + * @endcode + */ +void *parcMemory_Allocate(const size_t size); + +/** + * Performs the same operation as `Allocate` and then sets each byte of the allocated memory to zero. + * + * @param [in] size The number of bytes to allocate. + * + * @return A `void *` pointer set to the address of the allocated memory. + * @return NULL Memory allocation error. + * + * Example: + * @code + * { + * void *allocatedMemory; + * + * allocatedMemory = parcMemory_AllocateAndClear(100); + * if (allocatedMemory == NULL) + * // allocation failed + * } + * } + * @endcode + * + * @see parcMemory_Allocate + */ +void *parcMemory_AllocateAndClear(const size_t size); + +/** + * Allocate aligned memory. + * + * Allocates @p size bytes of memory such that the allocation's + * base address is an exact multiple of alignment, + * and returns the allocation in the value pointed to by @p pointer. + * + * The requested alignment must be a power of 2 greater than or equal to `sizeof(void *)`. + * + * Memory that is allocated can be used as an argument in subsequent call `Reallocate`, however + * `Reallocate` is not guaranteed to preserve the original alignment. + * + * @param [out] pointer A pointer to a `void *` pointer that will be set to the address of the allocated memory. + * @param [in] alignment A power of 2 greater than or equal to `sizeof(void *)` + * @param [in] size The number of bytes to allocate. + * + * @return 0 Successful + * @return EINVAL The alignment parameter is not a power of 2 at least as large as sizeof(void *) + * @return ENOMEM Memory allocation error. + * + * Example: + * @code + * { + * void *allocatedMemory; + * + * int failure = parcMemory_MemAlign(&allocatedMemory, sizeof(void *), 100); + * if (failure == 0) { + * parcMemory_Deallocate(&allocatedMemory); + * // allocatedMemory is now equal to zero. + * } + * } + * @endcode + * @see `posix_memalign` + */ +int parcMemory_MemAlign(void **pointer, const size_t alignment, const size_t size); + +/** + * Deallocate memory previously allocated via `Allocate` or `AllocateAndClear`. + * + * @param [in,out] pointer A pointer to a `void *` pointer to the address of the allocated memory that will be set to zero. + * + * Example: + * @code + * { + * void *allocatedMemory; + * + * allocatedMemory = parcMemory_Allocate(100); + * if (allocatedMemory == NULL) { + * // allocation failed + * } + * } + * @endcode + * + * @see parcMemory_Allocate + * @see parcMemory_AllocateAndClear + */ +void parcMemory_DeallocateImpl(void **pointer); + +#define parcMemory_Deallocate(_pointer_) parcMemory_DeallocateImpl((void **) _pointer_) + +/** + * Try to change the size of the allocation pointed to by @p pointer to @p newSize, and returns ptr. + * If there is not enough room to enlarge the memory allocation pointed to by @p pointer, + * create a new allocation, + * copy as much of the old data pointed to by @p pointer as will fit to the new allocation, + * deallocate the old allocation, + * and return a pointer to the allocated memory. + * + * If @p pointer is `NULL`, + * simply invoke the {@link parcMemory_Allocate} function to allocate memory aligned to the value of `sizeof(void *)` of @p newSize bytes. + * If @p newSize is zero and @p pointer is not NULL, + * a new, minimum sized object is allocated and the original object is freed. + * + * When extending a region previously allocated with `AllocateAndClear`, + * the additional memory is not guaranteed to be zero-filled. + * + * @param [in] pointer A pointer to previously allocated memory, or NULL. + * @param [in] newSize The size of the allocated memory. + * + * @return non-NULL A pointer to allocated memory. + * @return NULL A an error occurred. + * + * Example: + * @code + * { + * void *allocatedMemory; + * + * allocateMemory = parcMemory_Allocate(100); + * + * allocatedMemory = parcMemory_Reallocate(allocatedMemory, sizeof(void *), 200); + * + * parcMemory_Deallocate(&allocatedMemory); + * } + * @endcode + * + * @see parcMemory_Deallocate + * @see parcMemory_Allocate + */ +void *parcMemory_Reallocate(void *pointer, size_t newSize); + +/** + * Allocate sufficient memory for a copy of the string @p string, + * copy at most n characters from the string @p string into the allocated memory, + * and return the pointer to allocated memory. + * + * The copied string is always null-terminated. + * + * @param [in] string A pointer to a null-terminated string. + * @param [in] length The maximum allowed length of the resulting copy. + * + * @return non-NULL A pointer to allocated memory. + * @return NULL A an error occurred. + * + * Example: + * @code + * { + * char *string = "this is a string"; + * char *copy = parcMemory_StringDuplicate(string, strlen(string)); + * + * if (copy != NULL) { + * . . . + * parcMemory_Deallocate(©); + * } + * } + * @endcode + * + * @see {@link parcMemory_Deallocate()} + */ +char *parcMemory_StringDuplicate(const char *string, const size_t length); + +/** + * Return the number of outstanding allocations managed by this allocator. + * + * When you allocate memory, this count goes up by one. When you deallocate, it goes down by one. + * A well-behaved program will terminate with a call to parcMemory_Outstanding() returning 0. + * + * @return The number of memory allocations still outstanding (remaining to be deallocated). + * + * Example: + * @code + * { + * uint32_t numberOfAllocations = parcMemory_Outstanding(); + * } + * @endcode + */ +uint32_t parcMemory_Outstanding(void); + +/** + * Round up a given number of bytes to be a multiple of the cache line size on the target computer. + * + * @param [in] size The number of bytes to round up. + * + * @return The number of bytes that are a multiple of the cache line size on the target computer. + * + * Example: + * @code + * { + * size_t size = parcMemory_RoundUpToCacheLine(14); + * } + * @endcode + * + * @see parcMemory_RoundUpToMultiple + */ +size_t parcMemory_RoundUpToCacheLine(const size_t size); + +/** + * Round up a given number of bytes to be an even multiple. + * + * @param [in] size The number of bytes to round up. + * @param [in] multiple The number of bytes to round up. + * + * @return The number of bytes that are an even multiple of @p multiple. + * + * Example: + * @code + * { + * size_t size = parcMemory_RoundUp(14, 20); + * } + * @endcode + * + * @see parcMemory_RoundUpToCacheLine + */ +size_t parcMemory_RoundUpToMultiple(size_t size, size_t multiple); + +/** + * @def parcMemory_SafeFree + * + * Free only non-null pointers to memory + * + * @param memory A pointer to allocated memory + * + */ +#define parcMemory_SafeFree(memory) do { if (memory != NULL) { parcMemory_Deallocate(& (memory)); } } while (0) + +/** + * Allocate a printf(3) style formatted string. + * The result must be deallocated via `parcMemory_Deallocate` + * + * This function is equivalent to the `asprintf(3)` function in the standard library. + * + * @param [in] format A pointer to nul-terminated C string containing a printf style format specification. + * + * @return non-NULL A pointer to allocated memory containing the formatted string. + * @return NULL An error occurred. + * + * Example: + * @code + * { + * char *string = parcMemory_Format("Hello %s", "World"); + * + * parcMemory_Deallocated(&string); + * } + * @endcode + */ +char *parcMemory_Format(const char *format, ...); +#endif // libparc_parc_Memory_h diff --git a/libparc/parc/algol/parc_Network.c b/libparc/parc/algol/parc_Network.c new file mode 100644 index 00000000..35279648 --- /dev/null +++ b/libparc/parc/algol/parc_Network.c @@ -0,0 +1,390 @@ +/* + * 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 <LongBow/runtime.h> + +#include <sys/socket.h> +#include <ctype.h> +#include <sys/types.h> +#include <netdb.h> + +#include <parc/algol/parc_Network.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_BufferComposer.h> +#include <parc/algol/parc_Buffer.h> +#include <parc/algol/parc_URI.h> + +struct sockaddr * +parcNetwork_SockAddress(const char *address, in_port_t port) +{ + // this is the final return value from the function + struct sockaddr *addr = NULL; + + struct addrinfo hints; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = PF_UNSPEC; + hints.ai_flags = AI_ADDRCONFIG; + + // getaddrinfo allocates this pointer + struct addrinfo *ai; + int failure = getaddrinfo(address, NULL, &hints, &ai); + if (failure == 0) { + switch (ai->ai_family) { + case PF_INET: { + struct sockaddr_in *result = parcMemory_AllocateAndClear(sizeof(struct sockaddr_in)); + assertNotNull(result, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(struct sockaddr_in)); + if (result != NULL) { + assertTrue(ai->ai_addrlen == sizeof(struct sockaddr_in), + "Sockaddr wrong length, expected %zu got %u", sizeof(struct sockaddr_in), ai->ai_addrlen); + memcpy(result, ai->ai_addr, ai->ai_addrlen); + result->sin_port = htons(port); + addr = (struct sockaddr *) result; + } + break; + } + + case PF_INET6: { + struct sockaddr_in6 *result = parcMemory_AllocateAndClear(sizeof(struct sockaddr_in6)); + assertNotNull(result, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(struct sockaddr_in6)); + if (result != NULL) { + assertTrue(ai->ai_addrlen == sizeof(struct sockaddr_in6), + "Sockaddr wrong length, expected %zu got %u", sizeof(struct sockaddr_in6), ai->ai_addrlen); + + memcpy(result, ai->ai_addr, ai->ai_addrlen); + result->sin6_port = htons(port); + result->sin6_flowinfo = 0; + result->sin6_scope_id = 0; + addr = (struct sockaddr *) result; + } + break; + } + + default: { + // unsupported protocol + addr = NULL; + break; + } + } + + freeaddrinfo(ai); + } + + return addr; +} + +struct sockaddr_in * +parcNetwork_SockInet4Address(const char *address, in_port_t port) +{ + struct sockaddr_in *result = parcMemory_AllocateAndClear(sizeof(struct sockaddr_in)); + assertNotNull(result, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(struct sockaddr_in)); + if (result != NULL) { + result->sin_family = AF_INET; + result->sin_port = htons(port); +#if defined(SIN6_LEN) + result->sin_len = sizeof(struct sockaddr_in); +#endif + if (inet_pton(AF_INET, address, &(result->sin_addr)) == 1) { + return result; + } + parcMemory_Deallocate((void **) &result); + } + + return NULL; +} + +struct sockaddr_in6 * +parcNetwork_SockInet6Address(const char *address, in_port_t port, uint32_t flowInfo, uint32_t scopeId) +{ + struct sockaddr_in6 *result = parcMemory_AllocateAndClear(sizeof(struct sockaddr_in6)); + assertNotNull(result, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(struct sockaddr_in6)); + if (result != NULL) { + result->sin6_family = AF_INET6; + result->sin6_port = htons(port); + result->sin6_flowinfo = flowInfo; + result->sin6_scope_id = scopeId; +#if defined(SIN6_LEN) + result->sin6_len = sizeof(struct sockaddr_in6); +#endif + + if (inet_pton(AF_INET6, address, &(result->sin6_addr)) == 1) { + return result; + } + parcMemory_Deallocate((void **) &result); + } + + return NULL; +} + +struct sockaddr_in * +parcNetwork_SockInet4AddressAny() +{ + struct sockaddr_in *result = parcMemory_AllocateAndClear(sizeof(struct sockaddr_in)); + assertNotNull(result, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(struct sockaddr_in)); + if (result != NULL) { + result->sin_family = AF_INET; + result->sin_addr.s_addr = INADDR_ANY; +#if defined(SIN6_LEN) + result->sin_len = sizeof(struct sockaddr_in); +#endif + return result; + } + return NULL; +} + +PARCBufferComposer * +parcNetwork_SockInet4Address_BuildString(const struct sockaddr_in *address, PARCBufferComposer *composer) +{ + assertNotNull(address, "Parameter must be a non-null pointer to a struct sockaddr_in."); + + if (address->sin_family != AF_INET) { + trapIllegalValue(address->sin_family, "Expected an AF_INET configured address, not %d", address->sin_family); + } + + char buffer[INET_ADDRSTRLEN]; + inet_ntop(address->sin_family, &address->sin_addr, buffer, INET_ADDRSTRLEN); + parcBufferComposer_Format(composer, "inet4://%s:%u", buffer, ntohs(address->sin_port)); + + return composer; +} + +PARCBufferComposer * +parcNetwork_SockInet6Address_BuildString(const struct sockaddr_in6 *address, PARCBufferComposer *composer) +{ + assertNotNull(address, "Parameter must be a non-null pointer to a struct sockaddr_in."); + + if (address->sin6_family != AF_INET6) { + trapIllegalValue(address->sin_family, "Expected an AF_INET6 configured address, not %d", address->sin6_family); + } + + char buffer[INET6_ADDRSTRLEN]; + inet_ntop(address->sin6_family, &address->sin6_addr, buffer, INET6_ADDRSTRLEN); + + parcBufferComposer_Format(composer, "inet6://[%s%%%u]:%u", + buffer, + address->sin6_scope_id, + ntohs(address->sin6_port)); + + return composer; +} + +PARCBufferComposer * +parcNetwork_LinkAddress_BuildString(const unsigned char *address, size_t length, PARCBufferComposer *composer) +{ + parcBufferComposer_PutString(composer, "link://"); + for (size_t i = 0; i < length; i++) { + if (i > 0) { + parcBufferComposer_PutString(composer, "-"); + } + parcBufferComposer_Format(composer, "%02x", address[i]); + } + return composer; +} + +struct sockaddr_in * +parcNetwork_ParseInet4Address(const char *addressURI) +{ + struct sockaddr_in *result = NULL; + + PARCURI *uri = parcURI_Parse(addressURI); + if (strcmp("inet4", parcURI_GetScheme(uri)) == 0) { + } + parcURI_Release(&uri); + + return result; +} + +static PARCBuffer * +_parseMAC48AddressDashOrColon(const char *address, PARCBuffer *result) +{ + char *nextP = NULL; + + const char *p = address; + + int i = 0; + for (i = 0; i < 6; i++) { + long value = strtol(p, &nextP, 16); + if (nextP == p || (*nextP != ':' && *nextP != '-' && *nextP != 0)) { + result = NULL; + break; + } + parcBuffer_PutUint8(result, (uint8_t) value); + + p = nextP + 1; + } + + if (i != 6) { + result = NULL; + } + + return result; +} + +static PARCBuffer * +_parseMAC48AddressDot(const char *address, PARCBuffer *result) +{ + char *nextP = NULL; + + const char *p = address; + + int i = 0; + for (i = 0; i < 3; i++) { + long value = strtol(p, &nextP, 16); + if (nextP == p || (*nextP != '.' && *nextP != 0)) { + result = NULL; + break; + } + parcBuffer_PutUint16(result, (uint16_t) value); + + p = nextP + 1; + } + + if (i != 3) { + result = NULL; + } + + return result; +} + +bool +parcNetwork_ParseMAC48Address(const char *address, PARCBuffer *buffer) +{ + bool result = false; + + size_t originalPosition = parcBuffer_Position(buffer); + + if (strchr(address, '-') != NULL || strchr(address, ':') != NULL) { + if (_parseMAC48AddressDashOrColon(address, buffer) != NULL) { + result = true; + } + } + + if (strchr(address, '.') != NULL) { + if (_parseMAC48AddressDot(address, buffer) != NULL) { + result = true; + } + ; + } + + if (result == false) { + parcBuffer_SetPosition(buffer, originalPosition); + } + + return result; +} + +PARCBuffer * +parcNetwork_ParseLinkAddress(const char *address) +{ + if (strncmp("link://", address, 7) == 0) { + PARCBuffer *result = parcBuffer_Allocate(7); + + if (parcNetwork_ParseMAC48Address(&address[7], result) == false) { + parcBuffer_Release(&result); + trapIllegalValue(address, "Syntax error '%s'", address); + } + + return parcBuffer_Flip(result); + } + + trapIllegalValue(address, "Bad scheme '%s'", address); +} + +bool +parcNetwork_Inet4Equals(const struct sockaddr_in *x, const struct sockaddr_in *y) +{ + if (x == y) { + return true; + } + if (x == NULL || y == NULL) { + return false; + } + if (x->sin_family == y->sin_family) { + if (x->sin_addr.s_addr == y->sin_addr.s_addr) { + if (x->sin_port == y->sin_port) { + return true; + } + } + } + + return false; +} + +/* + * Returns true for ::1 address, false otherwise + * + * The address is in network byte order + * + * @return true The address is local + * @return false The address is not local + */ +static bool +_isInet6Loopback(struct sockaddr_in6 *sin6) +{ + if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) { + return true; + } + return false; +} + +/** + * Returns true if the address is on the 127.0.0.0/8 network + * + * The address is expected to be in network byte order + * + * @param [in] sin4 A pointer to a sockaddr_in containing an IPv4 address. + * + * @return true Address on 127.0.0.0/8 + * @return false Not a 127.0.0.0/8 + */ +static bool +_isInet4Loopback(struct sockaddr_in *sin4) +{ + uint32_t hostorder = htonl(sin4->sin_addr.s_addr); + if ((hostorder & 0xFF000000) == 0x7F000000) { + return true; + } + return false; +} + +bool +parcNetwork_IsSocketLocal(struct sockaddr *sock) +{ + assertNotNull(sock, "Parameter sock must be non-null"); + bool isLocal = false; + + switch (sock->sa_family) { + case PF_LOCAL: + isLocal = true; + break; + + case PF_INET: + isLocal = _isInet4Loopback((struct sockaddr_in *) sock); + break; + + case PF_INET6: + isLocal = _isInet6Loopback((struct sockaddr_in6 *) sock); + break; + + default: + break; + } + + return isLocal; +} diff --git a/libparc/parc/algol/parc_Network.h b/libparc/parc/algol/parc_Network.h new file mode 100644 index 00000000..58b8b1f3 --- /dev/null +++ b/libparc/parc/algol/parc_Network.h @@ -0,0 +1,299 @@ +/* + * 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. + */ + +/** + * @file parc_Network.h + * @ingroup networking + * @brief Basic Networking Support + * + */ +#ifndef libparc_parc_Networking_h +#define libparc_parc_Networking_h + +#include <arpa/inet.h> +#include <netinet/in.h> + +#include <parc/algol/parc_BufferComposer.h> +#include <parc/algol/parc_Buffer.h> + +#ifndef INPORT_ANY +#define INPORT_ANY 0 +#endif + +#ifdef _ANDROID_ +typedef uint16_t in_port_t; +#endif + +/** + * Parses any string in to an address, if possible + * + * The string may be an IPv6, IPv6 or hostname. If the string does not match an IPv4 or IPv6 nominal format, + * it will try to resolve the string as a hostname. If that fails, the function will return NULL. + * + * IMPORTANT: the returned pointer is allocated with <code>parcMemory_Allocate()</code> and you must use <code>parcMemory_Deallocate()</code>. + * + * @param [in] address An IPv4, IPv6, or hostname + * @param [in] port the port address + * + * @return NULL Could not parse the address or resolve as a hostname + * @return non-NULL a valid sockaddr. You shoule examine `sockaddr->sa_family` to determine IPv4 or IPv6. + * + * Example: + * @code + * { + * struct sockaddr *addr; + * addr = parcNetwork_SockAddress("1.2.3.4", 555); + * assertTrue(addr && addr->sa_family == AF_INET, "Addr not IPv4 for a dotted quad."); + * struct sockaddr_in *addr_in = (struct sockaddr_in *) addr; + * // ... + * parcMemory_Deallocate((void **)&addr); + * + * addr = parcNetwork_SockAddress("fe80::aa20:66ff:fe00:314a", 555); + * assertTrue(addr && addr->sa_family == AF_INET6, "Addr not IPv6."); + * struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *) addr; + * // ... + * parcMemory_Deallocate((void **)&addr); + * + * addr = parcNetwork_SockAddress("alpha.parc.com", 555); + * assertTrue(addr && addr->sa_family == AF_INET, "Addr not IPv4 hostname with only ipv4 address."); + * // ... + * parcMemory_Deallocate((void **)&addr); + * + * addr = parcNetwork_SockAddress("Over the rainbow, way up high", 555); + * assertNull(addr, "Addr no null for bogus name."); + * + * } + * @endcode + */ +struct sockaddr *parcNetwork_SockAddress(const char *address, in_port_t port); + +/** + * Compose an allocated sockaddr_in structure. + * + * + * @param [in] address An IPv4, IPv6, or hostname + * @param [in] port the port address + * @return A pointer to an allocated struct sockaddr_in which must be freed by <code>parcMemory_Deallocate()</code>, or NULL on error. + * + * Example: + * @code + * <#example#> + * @endcode + */ +struct sockaddr_in *parcNetwork_SockInet4Address(const char *address, in_port_t port); + +/** + * Compose an allocated sockaddr_in6 structure. + * + * + * @param [in] address An IPv4, IPv6, or hostname + * @param [in] port the port address + * @param flowInfo + * @param scopeId + * @return A pointer to an allocated sockaddr_in6 structure that must be freed via parcMemory_Deallocate(), or NULL on error. + * + * Example: + * @code + * <#example#> + * @endcode + */ +struct sockaddr_in6 *parcNetwork_SockInet6Address(const char *address, in_port_t port, uint32_t flowInfo, uint32_t scopeId); + +/** + * Allocate a struct sockaddr_in + * + * @return A pointer to an allocated struct sockaddr_in which must be freed by {@link parcMemory_Deallocate()}, or NULL on error. + * + * Example: + * @code + * <#example#> + * @endcode + */ +struct sockaddr_in *parcNetwork_SockInet4AddressAny(void); + +/** + * Append the string representation of the `struct sockaddr_in` to a {@link PARCBufferComposer}. + * + * The position of the `PARCBufferComposer` is incremented by the number characters necessary to represent the `struct sockaddr_in` + * + * @param [in] address An IPv4, IPv6, or hostname + * @param [in,out] composer The instance of `PARCBufferComposer` to be modified. + * @return return + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCBufferComposer *parcNetwork_SockInet4Address_BuildString(const struct sockaddr_in *address, PARCBufferComposer *composer); + +/** + * Append the string representation of the `struct sockaddr_in6` to a `PARCBufferComposer`. + * + * The position of the `PARCBufferComposer` is incremented by the number characters necessary to represent the `struct sockaddr_in6` + * + * + * @param [in] address A pointer to the spckaddr_in6 to append. + * @param [in,out] composer a POinter to the `PARCBufferComposer` to modify. + * @return A pointer to the @p composer. + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCBufferComposer *parcNetwork_SockInet6Address_BuildString(const struct sockaddr_in6 *address, PARCBufferComposer *composer); + +/** + * The standard (IEEE 802) format for printing MAC-48 addresses in human-friendly form is six groups of two hexadecimal digits, + * separated by hyphens (-) or colons (:), in transmission order (e.g. `01-23-45-67-89-ab` or `01:23:45:67:89:ab`). + * This form is also commonly used for EUI-64. + * Another convention used by networking equipment uses three groups of four hexadecimal digits separated by dots (.) + * (e.g. `0123.4567.89ab` ), again in transmission order. + * + * @param [in] address A pointer to the adress of the string. + * @param [in] length The size of the buffer + * @param [in] composer A pointer to the buffer. + * @return A pointer to the `PARCBufferComposer` containing the built string. + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCBufferComposer *parcNetwork_LinkAddress_BuildString(const unsigned char *address, size_t length, PARCBufferComposer *composer); + +/** + * Parse a link address, expressed as a nul-terminated C string of the form + * `link://` followed by hexadecimal numbers representing single byte values, + * each value separated by either a ':' or '-' character. + * + * The standard (IEEE 802) format for printing MAC-48 addresses in human-friendly form is six groups of two hexadecimal digits, + * separated by hyphens (-) or colons (:), in transmission order (e.g. `01-23-45-67-89-ab` or `01:23:45:67:89:ab`). + * This form is also commonly used for EUI-64. + * + * Another convention used by networking equipment uses three groups of four hexadecimal digits separated by dots (.) + * (e.g. `0123.4567.89ab` ), again in transmission order. + * + * @param address A null-terminated C string of the form `link://` followed single byte values separated by ':' or '-'. + * @return A PARCBuffer instance containing the parsed bytes of the link address. + * + * Example: + * @code + * { + * + * PARCBuffer *address = parcNetwork_ParseLinkAddress("link://32-00-14-06-30-60"); + * + * } + * @endcode + * + * @see parcNetwork_ParseMAC48Address + */ +PARCBuffer *parcNetwork_ParseLinkAddress(const char *address); + +/** + * Parse a MAC-48 address, expressed as a nul-terminated C string of the standard (IEEE 802) + * format for printing MAC-48 addresses in human-friendly form is six groups of two hexadecimal digits, + * separated by hyphens (-) or colons (:), in transmission order (e.g. `01-23-45-67-89-ab` or `01:23:45:67:89:ab`). + * This form is also commonly used for EUI-64. + * + * Another convention used by networking equipment uses three groups of four hexadecimal digits separated by dots (.) + * (e.g. `0123.4567.89ab` ), again in transmission order. + * + * @param [in] address A null-terminated C string of the form `link://` followed single byte values separated by ':' or '-'. + * @param [out] result A pointer to a `PARCBuffer` for the result. + * @return `true` If the address was successfully parsed. + * @return `false` if the address was not successfully parsed. The output PARCBuffer is unmodified. + * + * Example: + * @code + * { + * + * PARCBuffer *address = parcNetwork_ParseMAC48Address("32-00-14-06-30-60"); + * + * } + * @endcode + */ +bool parcNetwork_ParseMAC48Address(const char *address, PARCBuffer *result); + +/** + * Return the address parsed from @p addressURI. + * + * @param [in] addressURI + * @return A pointer to the `sockaddr_in`. + * + * Example: + * @code + * <#example#> + * @endcode + */ +struct sockaddr_in *parcNetwork_ParseInet4Address(const char *addressURI); + +/** + * Determines if a socket is local + * + * A socket is local if: (a) PF_LOCAL/AF_UNIX, or (b) PF_INET and on the 127.0.0.0/8 network, + * or (c) PF_INET6 and equal to the ::1 address. Anything else is considered non-local. + * + * @param [in] sock A socket structure + * + * @return `true` The socket represents a local/loopback address + * @return `false` The socket is not a local/loopback address + * + * Example: + * @code + * { + * struct sockaddr *s = parcNetwork_SockAddress("127.1.1.1", 5900); + * assertTrue(parcNetwork_IsSocketLocal(s), "This will not assert"); + * parcMemory_Deallocate((void **)&s); + * } + * @endcode + */ +bool parcNetwork_IsSocketLocal(struct sockaddr *sock); + +/** + * Return true if two `sockaddr_in` instances are equal. + * + * The equality function that this evaluates must implement the following equivalence relations on non-null instances: + * + * * It is reflexive: for any non-null reference value x, parcNetwork_Inet4Equals(x, x) must return true. + * + * * It is symmetric: for any non-null reference values x and y, parcNetwork_Inet4Equals(x, y) must return true if and only if + * parcNetwork_Inet4Equals(y x) returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * parcNetwork_Inet4Equals(x, y) returns true and + * parcNetwork_Inet4Equals(y, z) returns true, + * then parcNetwork_Inet4Equals(x, z) must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of parcNetwork_Inet4Equals(x, y) + * consistently return true or consistently return false. + * + * * For any non-null reference value x, equals(x, NULL)) must return false. + * + * @param [in] x A pointer to a struct sockaddr_in instance. + * @param [in] y A pointer to a struct sockaddr_in instance.s + * @return true the given sockaddr_in instances are equal. + * @return false the given sockaddr_in instances are not equal. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +bool parcNetwork_Inet4Equals(const struct sockaddr_in *x, const struct sockaddr_in *y); +#endif // libparc_parc_Networking_h diff --git a/libparc/parc/algol/parc_Object.c b/libparc/parc/algol/parc_Object.c new file mode 100644 index 00000000..cd8acd45 --- /dev/null +++ b/libparc/parc/algol/parc_Object.c @@ -0,0 +1,952 @@ +/* + * 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 <LongBow/runtime.h> + +#include <stdio.h> +#include <inttypes.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <sys/errno.h> +#include <pthread.h> +#include <time.h> +#include <sys/time.h> + +#include <stdint.h> +#include <stdbool.h> + +#include <parc/algol/parc_DisplayIndented.h> +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Hash.h> +#include <parc/concurrent/parc_AtomicUint64.h> + +typedef struct parc_object_locking { + pthread_mutex_t lock; + pthread_cond_t notification; + pthread_t locker; +} _PARCObjectLocking; + +/* + * This is the per-object header. + * The size of this structure must be less than or equal to the value used in the parcObject_PrefixLength macro. + */ +typedef struct object_header { +#define PARCObject_HEADER_MAGIC_GUARD_NUMBER 0x0ddFadda + uint32_t magicGuardNumber; + bool isAllocated; + bool barrier; + PARCReferenceCount references; + const PARCObjectDescriptor *descriptor; + + // The locking member points to the locking structure or is NULL if the object does not support locking. + _PARCObjectLocking *locking; + + // Currently every object is lockable, but at some point in the future this will be controlled by the descriptor. + _PARCObjectLocking lock; + + void *data[]; +} _PARCObjectHeader; + +/* + * Increment/decrement a pointer by the given value. + * + * @param [in] base The base pointer. + * @param [in] increment The value of the pointer increment. + * + * @return void* The updated pointer + */ +static inline void * +_pointerAdd(const void *base, const size_t increment) +{ + void *result = (void *) &((char *) base)[increment]; + return result; +} + +/* + * Compute the size of the prefix as the number of bytes necessary + * to fit the object header (with padding), and any additional per-object data, into allocated memory, + * such that the first address after the header and any additional per-object data, + * is aligned according to the value of the alignment parameter. + * + * For example, if the object header is 5 bytes long, and the alignment is 4 (bytes), + * then the necessary number of bytes to fit the header + * (and yet maintain the proper alignment for the first memory location after the header) is 8. + * + * @param [in] alignment Cache alignment + * + * @return The size of the object header as the number of bytes necessary to fit the header (with padding) into allocated memory. + */ +static inline size_t +_parcObject_PrefixLength(const PARCObjectDescriptor *descriptor) +{ + return parcObject_PrefixLength(descriptor->objectAlignment); +} + +/* + * Given the memory address, return a pointer to the corresponding _PARCObjectHeader structure. + * + * @param [in] object The base pointer to the object. + * + * @return A _PARCObjectHeader struct encapsulating the object's header information. + */ +static inline _PARCObjectHeader * +_parcObject_Header(const PARCObject *object) +{ + return _pointerAdd(object, -sizeof(_PARCObjectHeader)); +} + +static inline const PARCObjectDescriptor * +_parcObject_Descriptor(const PARCObject *object) +{ + return (_parcObject_Header(object)->descriptor); +} + +static inline _PARCObjectLocking * +_parcObjectHeader_Locking(const PARCObject *object) +{ + return _parcObject_Header(object)->locking; +} + +static inline bool +_parcObjectHeader_IsValid(const _PARCObjectHeader *header, const PARCObject *object) +{ + bool result = true; + + if ((intptr_t) header >= (intptr_t) object) { + result = false; + } else if (header->magicGuardNumber != PARCObject_HEADER_MAGIC_GUARD_NUMBER) { + result = false; + } else if (header->references == 0) { + result = false; + } + + return result; +} + +/* + * Compute the origin of the allocated memory. + * + * This will be greater than the start of the object header. + * due to alignment requirements causing the object header to be offset from the origin. + */ +static inline void * +_parcObject_Origin(const PARCObject *object) +{ + _PARCObjectHeader *header = _parcObject_Header(object); + + return _pointerAdd(object, -_parcObject_PrefixLength(header->descriptor)); +} + +static inline PARCObjectEquals * +_parcObject_ResolveEquals(const PARCObjectDescriptor *descriptor) +{ + while (descriptor->equals == NULL) { + descriptor = descriptor->super; + } + return descriptor->equals; +} + +static inline PARCObjectCopy * +_parcObject_ResolveCopy(const PARCObjectDescriptor *descriptor) +{ + while (descriptor->copy == NULL) { + descriptor = descriptor->super; + } + return descriptor->copy; +} + +static inline PARCObjectToString * +_parcObject_ResolveToString(const PARCObjectDescriptor *descriptor) +{ + while (descriptor->toString == NULL) { + descriptor = descriptor->super; + } + return descriptor->toString; +} + +static inline PARCObjectToJSON * +_parcObject_ResolveToJSON(const PARCObjectDescriptor *descriptor) +{ + while (descriptor->toJSON == NULL) { + descriptor = descriptor->super; + } + return descriptor->toJSON; +} + +static bool +_parcObject_Destructor(const PARCObjectDescriptor *descriptor, PARCObject **object) +{ + if (descriptor != NULL) { + if (descriptor->destructor != NULL) { + return descriptor->destructor(object); + } else if (descriptor->destroy != NULL) { + descriptor->destroy(object); + } + } + + return true; +} + +static int +_parcObject_Compare(const PARCObject *self, const PARCObject *other) +{ + _PARCObjectHeader *header = _parcObject_Header(self); + size_t length = header->descriptor->objectSize; + int result = memcmp(self, other, length); + return result; +} + +static PARCObject * +_parcObject_Copy(const PARCObject *object) +{ + _PARCObjectHeader *header = _parcObject_Header(object); + size_t length = header->descriptor->objectSize; + + void *result = parcObject_CreateInstanceImpl(header->descriptor); + memcpy(result, object, length); + parcObject_OptionalAssertValid(result); + return result; +} + +static bool +_parcObject_Equals(const PARCObject *x, const PARCObject *y) +{ + _PARCObjectHeader *header = _parcObject_Header(x); + + bool result = memcmp(x, y, header->descriptor->objectSize) == 0; + + return result; +} + +static char * +_parcObject_ToString(const PARCObject *object) +{ + _PARCObjectHeader *header = _parcObject_Header(object); + + char *string; + int nwritten = asprintf(&string, + "Object@%p { .references=%" PRId64 ", .objectLength = %zd, .objectAlignment=%u } data %p\n", + (void *) header, + header->references, header->descriptor->objectSize, header->descriptor->objectAlignment, object); + assertTrue(nwritten >= 0, "Error calling asprintf"); + char *result = parcMemory_StringDuplicate(string, strlen(string)); + free(string); + return result; +} + +static PARCJSON * +_parcObject_ToJSON(const PARCObject *object) +{ + _PARCObjectHeader *prefix = _parcObject_Header(object); + + PARCJSON *json = parcJSON_Create(); + + parcJSON_AddInteger(json, "references", prefix->references); + parcJSON_AddInteger(json, "objectLength", prefix->descriptor->objectSize); + parcJSON_AddInteger(json, "objectAlignment", prefix->descriptor->objectAlignment); + + char *addressString; + int nwritten = asprintf(&addressString, "%p", object); + assertTrue(nwritten >= 0, "Error calling asprintf"); + + parcJSON_AddString(json, "address", addressString); + + return json; +} + +static PARCHashCode +_parcObject_HashCode(const PARCObject *object) +{ + _PARCObjectHeader *header = _parcObject_Header(object); + return parcHashCode_Hash(object, header->descriptor->objectSize); +} + +static void +_parcObject_Display(const PARCObject *object, const int indentation) +{ + parcObject_OptionalAssertValid(object); + + _PARCObjectHeader *header = _parcObject_Header(object); + + parcDisplayIndented_PrintLine(indentation, "PARCObject@%p @%p={ .name=%s .references=%zd }\n", + object, header, header->descriptor->name, header->references); +} + +const PARCObjectDescriptor +parcObject_DescriptorName(PARCObject) = +{ + .name = "PARCObject", + .destroy = NULL, + .destructor = NULL, + .release = NULL, + .copy = _parcObject_Copy, + .toString = _parcObject_ToString, + .equals = _parcObject_Equals, + .compare = _parcObject_Compare, + .hashCode = _parcObject_HashCode, + .toJSON = _parcObject_ToJSON, + .display = _parcObject_Display, + .super = NULL, + .isLockable = true, + .objectSize = 0, + .objectAlignment = sizeof(void *) +}; + +bool +parcObject_IsValid(const PARCObject *object) +{ + bool result = true; + + if (object == NULL) { + result = false; + } else { + if (_parcObjectHeader_IsValid(_parcObject_Header(object), object) == false) { + result = false; + } + } + + return result; +} + +#ifdef PARCLibrary_DISABLE_VALIDATION +# define parcObjectHeader_OptionalAssertValid(_instance_) +#else +# define parcObjectHeader_OptionalAssertValid(_instance_) _parcObjectHeader_AssertValid(_instance_) +#endif + +static inline void +_parcObjectHeader_AssertValid(const _PARCObjectHeader *header, const PARCObject *object) +{ + trapIllegalValueIf(header->magicGuardNumber != PARCObject_HEADER_MAGIC_GUARD_NUMBER, "PARCObject@%p is corrupt.", object); + trapIllegalValueIf(header->descriptor == NULL, "PARCObject@%p descriptor cannot be NULL.", object); + if (header->descriptor->isLockable) { + trapIllegalValueIf(header->locking == NULL, "PARCObject@%p is corrupt. Is Lockable but no locking structure", object); + } +} + +static inline void +_parcObject_AssertValid(const PARCObject *object) +{ + trapIllegalValueIf(object == NULL, "PARCObject must be a non-null pointer."); + + _PARCObjectHeader *header = _parcObject_Header(object); + _parcObjectHeader_AssertValid(header, object); +} + +void +parcObject_AssertValid(const PARCObject *object) +{ + _parcObject_AssertValid(object); +} + +PARCObject * +parcObject_Acquire(const PARCObject *object) +{ + parcObject_OptionalAssertValid(object); + + _PARCObjectHeader *header = _parcObject_Header(object); + + parcAtomicUint64_Increment(&header->references); + + return (PARCObject *) object; +} + +static inline PARCObjectCompare * +_parcObject_ResolveCompare(const PARCObjectDescriptor *descriptor) +{ + while (descriptor->compare == NULL) { + descriptor = descriptor->super; + } + return descriptor->compare; +} + +int +parcObject_Compare(const PARCObject *x, const PARCObject *y) +{ + int result = 0; + if ((x == NULL) || (y == NULL)) { + result = 0; + if (x != NULL) { + result = 1; + } else if (y != NULL) { + result = -1; + } + return result; + } + + parcObject_OptionalAssertValid(x); + parcObject_OptionalAssertValid(y); + + PARCObjectCompare *compare = _parcObject_ResolveCompare(_parcObject_Descriptor(x)); + result = compare(x, y); + + return result; +} + +bool +parcObject_IsInstanceOf(const PARCObject *object, const PARCObjectDescriptor *descriptor) +{ + bool result = false; + + if (object != NULL) { + _PARCObjectHeader *header = _parcObject_Header(object); + + if (_parcObjectHeader_IsValid(header, object)) { + const PARCObjectDescriptor *d = _parcObject_Descriptor(object); + + while (result == false) { + if (d == descriptor) { + result = true; + } + d = d->super; + } + } + } + + return result; +} + +bool +parcObject_Equals(const PARCObject *x, const PARCObject *y) +{ + bool result = false; + + if (x == y) { + result = true; + } else if (x != NULL && y != NULL) { + _PARCObjectHeader *xHeader = _parcObject_Header(x); + _PARCObjectHeader *yHeader = _parcObject_Header(y); + + if (xHeader->descriptor == yHeader->descriptor) { + PARCObjectEquals *equals = _parcObject_ResolveEquals(xHeader->descriptor); + result = equals(x, y); + } + } + + return result; +} + +static inline PARCObjectHashCode * +_parcObject_ResolveHashCode(const PARCObjectDescriptor *descriptor) +{ + while (descriptor->hashCode == NULL) { + descriptor = descriptor->super; + } + return descriptor->hashCode; +} + +PARCHashCode +parcObject_HashCode(const PARCObject *object) +{ + parcObject_OptionalAssertValid(object); + + PARCObjectHashCode *hashCode = _parcObject_ResolveHashCode(_parcObject_Descriptor(object)); + + return hashCode(object); +} + +static inline PARCObjectDisplay * +_parcObject_ResolveDisplay(const PARCObjectDescriptor *descriptor) +{ + while (descriptor->display == NULL) { + descriptor = descriptor->super; + } + return descriptor->display; +} + +void +parcObject_Display(const PARCObject *object, const int indentation) +{ + parcObject_OptionalAssertValid(object); + + PARCObjectDisplay *display = _parcObject_ResolveDisplay(_parcObject_Descriptor(object)); + + display(object, indentation); +} + +char * +parcObject_ToString(const PARCObject *object) +{ + parcObject_OptionalAssertValid(object); + + PARCObjectToString *toString = _parcObject_ResolveToString(_parcObject_Descriptor(object)); + + return toString(object); +} + +PARCJSON * +parcObject_ToJSON(const PARCObject *object) +{ + parcObject_OptionalAssertValid(object); + + PARCObjectToJSON *toJSON = _parcObject_ResolveToJSON(_parcObject_Descriptor(object)); + return toJSON(object); +} + +PARCObject * +parcObject_CreateAndClearInstanceImpl(const PARCObjectDescriptor *descriptor) +{ + PARCObject *result = parcObject_CreateInstanceImpl(descriptor); + memset(result, 0, descriptor->objectSize); + return result; +} + +static pthread_once_t _parcObject_GlobalLockAttributesInitialized = PTHREAD_ONCE_INIT; +static pthread_mutexattr_t _parcObject_GlobalLockAttributes; + +static void +_parcObject_InitializeGobalLockAttributes(void) +{ + pthread_mutexattr_init(&_parcObject_GlobalLockAttributes); + pthread_mutexattr_settype(&_parcObject_GlobalLockAttributes, PTHREAD_MUTEX_ERRORCHECK); +} + +static inline void +_parcObject_InitializeLocking(_PARCObjectLocking *locking) +{ + if (locking != NULL) { + pthread_once(&_parcObject_GlobalLockAttributesInitialized, _parcObject_InitializeGobalLockAttributes); + + pthread_mutex_init(&locking->lock, &_parcObject_GlobalLockAttributes); + pthread_cond_init(&locking->notification, NULL); + + locking->locker = (pthread_t) NULL; + } +} + +static inline _PARCObjectHeader * +_parcObjectHeader_InitAllocated(_PARCObjectHeader *header, const PARCObjectDescriptor *descriptor) +{ + header->magicGuardNumber = PARCObject_HEADER_MAGIC_GUARD_NUMBER; + header->barrier = false; + header->references = 1; + header->descriptor = (PARCObjectDescriptor *) descriptor; + header->isAllocated = true; + + if (header->descriptor->isLockable) { + header->locking = &header->lock; + _parcObject_InitializeLocking(header->locking); + } else { + header->locking = NULL; + } + + return header; +} + +static inline _PARCObjectHeader * +_parcObjectHeader_InitUnallocated(_PARCObjectHeader *header, const PARCObjectDescriptor *descriptor) +{ + _parcObjectHeader_InitAllocated(header, descriptor); + header->isAllocated = false; + + return header; +} + +PARCObject * +parcObject_WrapImpl(void *memory, const PARCObjectDescriptor *descriptor) +{ + size_t prefixLength = _parcObject_PrefixLength(descriptor); + PARCObject *object = _pointerAdd(memory, prefixLength); + + _parcObjectHeader_InitUnallocated(_parcObject_Header(object), descriptor); + + return object; +} + +PARCObject * +parcObject_CreateInstanceImpl(const PARCObjectDescriptor *descriptor) +{ + size_t prefixLength = _parcObject_PrefixLength(descriptor); + size_t totalMemoryLength = prefixLength + descriptor->objectSize; + + void *origin = NULL; + parcMemory_MemAlign(&origin, sizeof(void *), totalMemoryLength); + + if (origin == NULL) { + errno = ENOMEM; + return NULL; + } + + PARCObject *object = _pointerAdd(origin, prefixLength); + + _parcObjectHeader_InitAllocated(_parcObject_Header(object), descriptor); + + errno = 0; + return object; +} + +PARCObject * +parcObject_InitInstanceImpl(PARCObject *object, const PARCObjectDescriptor *descriptor) +{ + _PARCObjectHeader *header = _parcObject_Header(object); + + _parcObjectHeader_InitUnallocated(header, descriptor); + return object; +} + +PARCObject * +parcObject_InitAndClearInstanceImpl(PARCObject *object, const PARCObjectDescriptor *descriptor) +{ + parcObject_InitInstanceImpl(object, descriptor); + + memset(object, 0, descriptor->objectSize); + return object; +} + +PARCObject * +parcObject_Copy(const PARCObject *object) +{ + parcObject_OptionalAssertValid(object); + + PARCObjectCopy *copy = _parcObject_ResolveCopy(_parcObject_Descriptor(object)); + return copy(object); +} + +PARCReferenceCount +parcObject_Release(PARCObject **objectPointer) +{ + PARCObject *object = *objectPointer; + parcObject_OptionalAssertValid(object); + + _PARCObjectHeader *header = _parcObject_Header(object); + + trapIllegalValueIf(header->references == 0, "PARCObject@%p references must be > 0", object); + + PARCReferenceCount result = parcAtomicUint64_Decrement(&header->references); + + if (result == 0) { + if (_parcObject_Destructor(header->descriptor, objectPointer)) { + if (header->locking != NULL) { + pthread_cond_destroy(&header->locking->notification); + } + if (header->isAllocated) { + void *origin = _parcObject_Origin(object); + parcMemory_Deallocate(&origin); + } + assertNotNull(*objectPointer, "Class implementation unnecessarily clears the object pointer."); + } else { + assertNull(*objectPointer, "Class implementation must clear the object pointer."); + } + } + + *objectPointer = NULL; + return result; +} + +PARCReferenceCount +parcObject_GetReferenceCount(const PARCObject *object) +{ + parcObject_OptionalAssertValid(object); + + _PARCObjectHeader *header = _parcObject_Header(object); + + return header->references; +} + +const PARCObjectDescriptor * +parcObject_GetDescriptor(const PARCObject *object) +{ + parcObject_OptionalAssertValid(object); + + _PARCObjectHeader *header = _parcObject_Header(object); + + return header->descriptor; +} + +const PARCObjectDescriptor * +parcObject_SetDescriptor(PARCObject *object, const PARCObjectDescriptor *descriptor) +{ + parcObject_OptionalAssertValid(object); + assertNotNull(descriptor, "PARCObjectDescriptor cannot be NULL."); + + _PARCObjectHeader *header = _parcObject_Header(object); + + const PARCObjectDescriptor *result = header->descriptor; + header->descriptor = (PARCObjectDescriptor *) descriptor; + + return result; +} + +PARCObjectDescriptor * +parcObjectDescriptor_Create(const char *name, + size_t objectSize, + unsigned int objectAlignment, + bool isLockable, + PARCObjectDestructor *destructor, + PARCObjectRelease *release, + PARCObjectCopy *copy, + PARCObjectToString *toString, + PARCObjectEquals *equals, + PARCObjectCompare *compare, + PARCObjectHashCode *hashCode, + PARCObjectToJSON *toJSON, + PARCObjectDisplay *display, + const PARCObjectDescriptor *superType, + PARCObjectTypeState *typeState) +{ + assertNotNull(superType, "Supertype descriptor cannot be NULL."); + + PARCObjectDescriptor *result = parcMemory_AllocateAndClear(sizeof(PARCObjectDescriptor)); + if (result != NULL) { + strncpy(result->name, name, sizeof(result->name) - 1); + result->name[sizeof(result->name) - 1] = 0; + result->destroy = NULL; + result->destructor = destructor; + result->release = release; + result->copy = copy; + result->toString = toString; + result->equals = equals; + result->compare = compare; + result->hashCode = hashCode; + result->toJSON = toJSON; + result->display = display; + result->super = superType; + result->objectSize = objectSize; + result->objectAlignment = objectAlignment; + result->typeState = typeState; + result->isLockable = isLockable; + } + return result; +} + +PARCObjectDescriptor * +parcObjectDescriptor_CreateExtension(const PARCObjectDescriptor *superType, const char *name) +{ + PARCObjectDescriptor *result = parcMemory_AllocateAndClear(sizeof(PARCObjectDescriptor)); + *result = *superType; + strncpy(result->name, name, sizeof(result->name) - 1); + result->name[sizeof(result->name) - 1] = 0; + return result; +} + +PARCObjectTypeState * +parcObjectDescriptor_GetTypeState(const PARCObjectDescriptor *descriptor) +{ + return descriptor->typeState; +} + +const PARCObjectDescriptor * +parcObjectDescriptor_GetSuperType(const PARCObjectDescriptor *descriptor) +{ + return descriptor->super; +} + +bool +parcObjectDescriptor_Destroy(PARCObjectDescriptor **descriptorPointer) +{ + parcMemory_Deallocate(descriptorPointer); + return true; +} + +bool +parcObject_Unlock(const PARCObject *object) +{ + bool result = false; + + parcObject_OptionalAssertValid(object); + + _PARCObjectHeader *header = _parcObject_Header(object); + if (header->references > 0) { + _parcObjectHeader_AssertValid(header, object); + + if (object != NULL) { + _PARCObjectLocking *locking = _parcObjectHeader_Locking(object); + if (locking != NULL) { + locking->locker = (pthread_t) NULL; + result = (pthread_mutex_unlock(&locking->lock) == 0); + + assertTrue(result, "Attempted to unlock an unowned lock."); + } + } + } + return result; +} + +bool +parcObject_Lock(const PARCObject *object) +{ +#ifndef _ANDROID_ + extern int errno; +#endif + bool result = false; + + parcObject_OptionalAssertValid(object); + + if (object != NULL) { + _PARCObjectLocking *locking = _parcObjectHeader_Locking(object); + if (locking != NULL) { + trapCannotObtainLockIf(pthread_equal(locking->locker, pthread_self()), + "Recursive locks on %p are not supported.", object); + + errno = pthread_mutex_lock(&locking->lock); + + if (errno == 0) { + locking->locker = pthread_self(); + result = true; + } + } + } + + return result; +} + +bool +parcObject_TryLock(const PARCObject *object) +{ + bool result = false; + + if (object != NULL) { + parcObject_OptionalAssertValid(object); + + _PARCObjectLocking *locking = _parcObjectHeader_Locking(object); + if (locking != NULL) { + trapCannotObtainLockIf(pthread_equal(locking->locker, pthread_self()), "Recursive locks are not supported."); + + int lockStatus = pthread_mutex_trylock(&locking->lock); + + if (lockStatus == 0) { + result = true; + locking->locker = pthread_self(); + } + } + } + + return result; +} + +bool +parcObject_IsLocked(const PARCObject *object) +{ + parcObject_OptionalAssertValid(object); + bool result = false; + + _PARCObjectLocking *locking = _parcObjectHeader_Locking(object); + if (locking != NULL) { + result = locking->locker != (pthread_t) NULL; + } + + return result; +} + +void +parcObject_Wait(const PARCObject *object) +{ + parcObject_OptionalAssertValid(object); + + _PARCObjectLocking *locking = _parcObjectHeader_Locking(object); + if (locking != NULL) { + pthread_cond_wait(&locking->notification, &locking->lock); + } +} + +bool +parcObject_WaitUntil(const PARCObject *object, const struct timespec *time) +{ + parcObject_OptionalAssertValid(object); + + bool result = false; + + _PARCObjectLocking *locking = _parcObjectHeader_Locking(object); + if (locking != NULL) { + int waitResult = pthread_cond_timedwait(&locking->notification, &locking->lock, time); + if (waitResult == 0) { + result = true; + } + } + + return result; +} + +bool +parcObject_WaitFor(const PARCObject *object, const uint64_t nanoSeconds) +{ + parcObject_OptionalAssertValid(object); + + bool result = false; + + struct timeval now; + gettimeofday(&now, NULL); + + // Convert timeval to timespec. + struct timespec time = { + .tv_sec = now.tv_sec, + .tv_nsec = (now.tv_usec * 1000) + }; + time.tv_nsec += nanoSeconds; + time.tv_sec += time.tv_nsec / 1000000000; + time.tv_nsec = time.tv_nsec % 1000000000; + + _PARCObjectLocking *locking = _parcObjectHeader_Locking(object); + if (locking != NULL) { + int waitResult = pthread_cond_timedwait(&locking->notification, &locking->lock, &time); + + if (waitResult == 0) { + result = true; + } + } + + return result; +} + +void +parcObject_Notify(const PARCObject *object) +{ + parcObject_OptionalAssertValid(object); + + _PARCObjectLocking *locking = _parcObjectHeader_Locking(object); + if (locking != NULL) { + pthread_cond_signal(&locking->notification); + } +} + +void +parcObject_NotifyAll(const PARCObject *object) +{ + parcObject_OptionalAssertValid(object); + + _PARCObjectLocking *locking = _parcObjectHeader_Locking(object); + if (locking != NULL) { + pthread_cond_broadcast(&locking->notification); + } +} + +bool +parcObject_BarrierSet(const PARCObject *object) +{ + _PARCObjectHeader *header = _parcObject_Header(object); + + while (__sync_bool_compare_and_swap(&header->barrier, false, true) == false) { + ; + } + return true; +} + +bool +parcObject_BarrierUnset(const PARCObject *object) +{ + _PARCObjectHeader *header = _parcObject_Header(object); + + while (__sync_bool_compare_and_swap(&header->barrier, true, false) == false) { + ; + } + + return false; +} diff --git a/libparc/parc/algol/parc_Object.h b/libparc/parc/algol/parc_Object.h new file mode 100644 index 00000000..0f3a932b --- /dev/null +++ b/libparc/parc/algol/parc_Object.h @@ -0,0 +1,1504 @@ +/* + * 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. + */ + +/** + * @file parc_Object.h + * @ingroup memory + * @brief Reference Counted Object Memory + * + * An arbitrary C structure stored in allocated memory with a reference counter. + * + * When a PARC Object is created, via `parcObject_Create`, or `parcObject_Copy` it has reference count of 1. + * + * References to a PARC Object are acquired by calling `parcObject_Acquire` + * and once a reference is no longer needed it is released via parcObject_Release`. + * When the last reference is released, the memory storing the PARC Object is deallocated. + * Any further reference to that object or its memory is undefined. + * + * When creating a PARCObject, the caller may supply an instance of `PARCObjectDescriptor` containing + * configuration information used and pointers to functions that are invoked during the lifecycle of the PARC Object. + * Notable functions that are a finalization cleanup function for an object that will be deallocated, + * a clone function for an object that is being cloned, + * and a string generator for an object that is implementing the `ToString` function. + * Implementors of modules that use PARCObject supply a specification of callback + * functions that implement specific behaviours for interfaces using PARC Object. + * + */ +#ifndef libparc_parc_Object_h +#define libparc_parc_Object_h + +#include <stdint.h> +#include <time.h> + +#include <LongBow/runtime.h> +#include <LongBow/longBow_Compiler.h> + +#include <parc/algol/parc_CMacro.h> +//#include <parc/algol/parc_JSON.h> +//#include <parc/algol/parc_HashCode.h> +#ifdef PARCLibrary_DISABLE_VALIDATION +# define parcObject_OptionalAssertValid(_instance_) +#else +# define parcObject_OptionalAssertValid(_instance_) parcObject_AssertValid(_instance_) +#endif + +//Switch to strong type after subclasses are converted +//typedef struct {int unused;} PARCObject; +typedef void PARCObject; + +#include <stdint.h> +#include <stdbool.h> + +typedef struct PARCObjectDescriptor PARCObjectDescriptor; + +/** + * @define parcObject_DescriptorName(_type) + * + * Compose the token for a subtype specific name for a subtype's `PARCObjectDescriptor` + * which is a parameter to `parcObject_Create`. + * + * For example + * @code + * parcObject_DescriptorName(MyType) + * @endcode + * + * generates the token `MyType_Descriptor` + * + * Implementations should use this macro, rather than hardcode the format in their code. + */ +#define parcObject_DescriptorName(_type) parcCMacro_Cat(_type, _Descriptor) + +/** + * @define parcObjectDescriptor_Declaration(_type_) + * + * Create a declaration of a `PARCObjectDescriptor` implementation. + * To define the actual implementation, use `parcObject_Override` + */ +#define parcObjectDescriptor_Declaration(_type_) const PARCObjectDescriptor parcObject_DescriptorName(_type_) + +/** + * @define parcObject_Declare(_type_) + * + * Create a declaration of a `PARCObject` implementation. + * This causes the corresponding `typedef` to be defined + * and the global PARCObjectDescriptor corresponding to the declared type. + */ +#define parcObject_Declare(_type_) \ + typedef struct _type_ _type_; \ + extern parcObjectDescriptor_Declaration(_type_) + +#include <parc/algol/parc_HashCode.h> +#include <parc/algol/parc_JSON.h> + +/** + * A Function that performs the final cleanup and resource deallocation when + * a PARC Object has no more references. + * + * This is deprecated and will be removed. + * Use `PARCObjectDestructor` + */ +typedef void (PARCObjectDestroy)(PARCObject **); + +/** + * A Function that performs the final cleanup and resource deallocation when + * a PARC Object has no more references. + * + * If the function returns `true` the object is automatically deallocated and destroyed. + * If the function returns `false` the object is not automatically deallocated and destroyed, + * and the responsibility for the object's state and memory are the responsibility of + * the `PARCObjectDestructor` function. + */ +typedef bool (PARCObjectDestructor)(PARCObject **); + +/** + * A Function that releases one reference to the given PARC Object. + * On the final release, where the number of references becomes 0, + * the PARCObjectDestroy function is invoked. + */ +typedef void (PARCObjectRelease)(PARCObject **objectPointer); + +/** + * A function that produces a deep-copy of the given PARC Object instance. + */ +typedef PARCObject *(PARCObjectCopy)(const PARCObject *object); + +/** + * A function that invokes the proper _Equals() function for two PARC Object instances. + */ +typedef bool (PARCObjectEquals)(const PARCObject *objectX, const PARCObject *objectY); + +/** + * A function that invokes the proper _Compare() functions for two PARC Object instances. + */ +typedef int (PARCObjectCompare)(const PARCObject *, const PARCObject *); + +/** + * A function that computes the 32-bit hashcode of the given PARC Object instance. + */ +typedef PARCHashCode (PARCObjectHashCode)(const PARCObject *object); + +/** + * A function that produces a C style nul-terminated string representation of the given PARC Object instance. + */ +typedef char *(PARCObjectToString)(const PARCObject *object); + +/** + * A function that displays a human readable representation of the given PARCObject. + */ +typedef void (PARCObjectDisplay)(const PARCObject *object, const int indentation); + +/** + * A function that generates a JSON representation of the given PARC Object instance. + */ +typedef PARCJSON *(PARCObjectToJSON)(const PARCObject *); + +/** + * Every PARCObject descriptor has a pointer to a `PARCObjectState` + * containing arbitrary data related to all instances sharing the descriptor. + */ +typedef void PARCObjectTypeState; + +/** + * Every PARC Object instance contains a pointer to an instance of this structure defining + * the canonical meta-data for the object. + */ +struct PARCObjectDescriptor { + char name[64]; + PARCObjectDestroy *destroy; + PARCObjectDestructor *destructor; + PARCObjectRelease *release; + PARCObjectCopy *copy; + PARCObjectToString *toString; + PARCObjectEquals *equals; + PARCObjectCompare *compare; + PARCObjectHashCode *hashCode; + PARCObjectToJSON *toJSON; + PARCObjectDisplay *display; + const struct PARCObjectDescriptor *super; + size_t objectSize; + unsigned objectAlignment; + bool isLockable; + PARCObjectTypeState *typeState; +}; + + +/** + * Create an allocated instance of `PARCObjectDescriptor`. + * + * @param [in] name A nul-terminated, C string containing the name of the object descriptor. + * @param [in] objectSize The number of bytes necessary to contain the object. + * @param [in] objectAlignment The alignment boundary necessary for the object, a power of 2 greater than or equal to `sizeof(void *)` + * @param [in] isLockable True, if this object implementation supports locking. + * @param [in] destructor The callback function to call when the last `parcObject_Release()` is invoked (replaces @p destroy). + * @param [in] release The callback function to call when `parcObject_Release()` is invoked. + * @param [in] copy The callback function to call when parcObject_Copy() is invoked. + * @param [in] toString The callback function to call when `parcObject_ToString()` is invoked. + * @param [in] equals The callback function to call when `parcObject_Equals()` is invoked. + * @param [in] compare The callback function to call when `parcObject_Compare()` is invoked. + * @param [in] hashCode The callback function to call when `parcObject_HashCode()` is invoked. + * @param [in] toJSON The callback function to call when `parcObject_ToJSON()` is invoked. + * @param [in] display The callback function to call when `parcObject_Display()` is invoked. + * @param [in] super A pointer to a `PARCObjectDescriptor` for the supertype of created `PARCObjectDescriptor` + * @param [in] typeState A pointer to a `PARCObjectTypeState` for the per-type data for the created `PARCObjectDescriptor` + * + * @return NULL Memory could not be allocated to store the `PARCObjectDescriptor` instance. + * @return non-NULL Successfully created the implementation + */ +PARCObjectDescriptor *parcObjectDescriptor_Create(const char *name, + size_t objectSize, + unsigned int objectAlignment, + bool isLockable, + PARCObjectDestructor *destructor, + PARCObjectRelease *release, + PARCObjectCopy *copy, + PARCObjectToString *toString, + PARCObjectEquals *equals, + PARCObjectCompare *compare, + PARCObjectHashCode *hashCode, + PARCObjectToJSON *toJSON, + PARCObjectDisplay *display, + const PARCObjectDescriptor *superType, + PARCObjectTypeState *typeState); + +PARCObjectDescriptor *parcObjectDescriptor_CreateExtension(const PARCObjectDescriptor *superType, const char *name); + +PARCObjectTypeState *parcObjectDescriptor_GetTypeState(const PARCObjectDescriptor *descriptor); + +const PARCObjectDescriptor *parcObjectDescriptor_GetSuperType(const PARCObjectDescriptor *descriptor); + +bool parcObjectDescriptor_Destroy(PARCObjectDescriptor **descriptorPointer); + +/** + * The globally available `PARCObject` descriptor. + */ +extern parcObjectDescriptor_Declaration(PARCObject); + +typedef uint64_t PARCReferenceCount; + +/** + * Assert that an instance of PARC Object is valid. + * + * If the instance is not valid, terminate via an assertion. + * + * Valid means the internal state of the type is consistent with its + * required current or future behaviour. + * This may include the validation of internal instances of types. + * + * @param [in] object A pointer to a PARC Object instance. + */ +void parcObject_AssertValid(const PARCObject *object); + +/** + * Determine if an instance of PARCObject is valid. + * + * A valid PARCObject has non-NULL, it has a reference count > 0, + * it is non-zero in length, and has a valid alignment. + * + * @param [in] object A pointer to a PARC Object instance. + * + * @return true The PARCObject is valid. + * @return true The PARCObject is invalid. + */ +bool parcObject_IsValid(const PARCObject *object); + +/** + * Create a new reference counted object that is a deep copy of the specified object, + * if possible, or, otherwise, a shallow copy of the object's total allocation memory. + * + * The reference count for the new object is 1. + * + * @param [in] object A pointer to the original object. + * + * @return NULL Memory could not be allocated. + * @return non-NULL A pointer to a new reference counted object. + * + * Example: + * @code + * { + * struct MyType *t = parcObject_Create(sizeof(struct MyType), &MyType_Descriptor); + * + * struct MyType *copy = parcObject_Copy(t); + * } + * @endcode + * + * @see parcObject_Release + */ +PARCObject *parcObject_Copy(const PARCObject *object); + +/** + * Compare two object instances. + * + * The comparison function in the first `PARCObjectDescriptor` parameter is used for comparison. + * The objects are expected to be of the same type. Thus, if the comparison function + * associated with the first `PARCObjectDescriptor` function is NULL, it is assumed the + * same holds for the second parameter. In this case, the instance pointers are compared. + * + * @param [in] x An object. + * @param [in] y An object. + * + * @return int The result of the comparison. + * + * Example: + * @code + * { + * struct MyType *t = parcObject_Create(sizeof(struct MyType), &MyType_Descriptor); + * + * int compareResult = parcObject_Compare(t, t); + * printf("0? %d\n", compareResult); + * + * parcObject_Release(&t); + * } + * @endcode + */ +int parcObject_Compare(const PARCObject *x, const PARCObject *y); + +/** + * Determine if two PARCObject instances are equal. + * + * Two PARCObject instances are equal if, and only if, the instance pointers are equal. + * + * The following equivalence relations on non-null `PARCObject` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, `PARCObject_Equals(x, x)` + * must return true. + * + * * It is symmetric: for any non-null reference values x and y, + * `parcObject_Equals(x, y)` must return true if and only if + * `parcObject_Equals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcObject_Equals(x, y)` returns true and + * `parcObject_Equals(y, z)` returns true, + * then `parcObject_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple + * invocations of `parcObject_Equals(x, y)` consistently return true or + * consistently return false. + * + * * For any non-null reference value x, `parcObject_Equals(x, NULL)` must + * return false. + * + * @param x A pointer to a `PARCObject` instance. + * @param y A pointer to a `PARCObject` instance. + * @return true if the two `PARCObject` instances are equal. + * + * Example: + * @code + * { + * struct MyType *a = parcObject_Create(sizeof(struct MyType), &MyType_Descriptor); + * struct MyType *b = parcObject_Create(sizeof(struct MyType), &MyType_Descriptor); + * + * if (parcObject_Equals(a, b)) { + * // true + * } else { + * // false + * } + * } + * @endcode + */ +bool parcObject_Equals(const PARCObject *x, const PARCObject *y); + +/** + * Retrieve the hashcode of the given object. + * + * If no object implementation is provided, the hashcode is the 32-bit address + * of the base object pointer. Otherwise, the hashcode is computed by the + * provided hashcode function. + * + * @param [in] object An object. + * + * @return uint32_t The object hashcode + * + * Example: + * @code + * { + * struct MyType *t = parcObject_Create(sizeof(struct MyType), &MyType_Descriptor); + * + * PARCHashCode hashcode = parcObject_HashCode(t); + * printf("Hashcode = %" PRIXPARCHashCode "\n", hashcode); + * + * parcObject_Release(&t); + * } + * @endcode + */ +PARCHashCode parcObject_HashCode(const PARCObject *object); + +/** + * Create a C string containing a human readable representation of the given object. + * + * @param [in] object The object from which a string representation will be generated. + * + * @return NULL Memory could not be allocated to contain the C string. + * @return non-NULL An allocated C string that must be deallocated via parcMemory_Deallocate(). + * + * Example: + * @code + * { + * struct MyType *t = parcObject_Create(sizeof(struct MyType), &MyType_Descriptor); + * + * char *string = parcObject_ToString(t); + * printf("%s\n", string); + * parcMemory_Deallocate((void **) &string); + * + * parcObject_Release(&t); + * } + * @endcode + */ +char *parcObject_ToString(const PARCObject *object); + +/** + * Create a `PARCJSON` instance (representation) of the given object. + * + * @param [in] object The object from which a JSON instance will be generated. + * + * @return NULL Memory could not be allocated to contain the `PARCJSON` instance. + * @return non-NULL An allocated C string that must be deallocated via parcMemory_Deallocate(). + * + * Example: + * @code + * { + * struct MyType *t = parcObject_Create(sizeof(struct MyType), &MyType_Descriptor); + * + * PARCJSON *json = parcObject_ToJSON(t); + * printf("JSON representation: %s\n", parcJSON_ToString(json)); + * parcJSON_Release(&json); + * + * parcObject_Release(&t); + * } + * @endcode + */ +PARCJSON *parcObject_ToJSON(const PARCObject *object); + +/** + * Acquire a new reference to an object. + * + * The reference count to the object is incremented. + * + * @param [in] object The object to which to refer. + * + * @return The same value as the input parameter @p object + * + * Example: + * @code + * { + * struct MyType *t = parcObject_Create(sizeof(struct MyType), &MyType_Descriptor); + * + * struct MyType *new = parcObject_Acquire(t); + * + * parcObject_Release(&t); + * parcObject_Release(&new); + * } + * @endcode + */ +PARCObject *parcObject_Acquire(const PARCObject *object); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If an invocation of `parcObject_Release` causes the last reference to + * the instance to be released, it calls the instance's `destructor` function + * specified in the `PARCObjectDescriptor` structure supplied when the instance + * was created (see `parcObject_Create`. + * + * The contents of the deallocated memory used for the PARC object are undefined. + * Do not reference the object after the last release. + * + * @param [in] objectPointer A pointer to a pointer to the instance to release. + * + * @return The number of remaining references to the object. + * + * Example: + * @code + * { + * struct MyType *t = parcObject_Create(sizeof(struct MyType), &MyType_Descriptor); + * + * parcObject_Release(&t); + * } + * @endcode + * + * @see parcObject_Create + * @see parcObject_Acquire + */ +PARCReferenceCount parcObject_Release(PARCObject **objectPointer); + +/** + * Get the current `PARCReferenceCount` for the specified object. + * + * The reference count must always be greater than zero. + * + * @param [in] object A pointer to a valid `PARCObject` instance. + * + * @return The current reference count for the specified object. + * + * Example: + * @code + * { + * struct MyType *t = parcObject_Create(sizeof(struct MyType), &MyType_Descriptor); + * + * PARCReferenceCount count = parcObject_GetReferenceCount(t); + * + * parcObject_Release(&t); + * } + * @endcode + * + * @see parcObject_Acquire + * @see parcObject_Release + */ +PARCReferenceCount parcObject_GetReferenceCount(const PARCObject *object); + +/** + * Print a human readable representation of the given `PARC Object. + * + * @param [in] object A pointer to the instance to display. + * @param [in] indentation The level of indentation to use to pretty-print the output. + * + * Example: + * @code + * { + * struct MyType *t = parcObject_Create(sizeof(struct MyType), &MyType_Descriptor); + * + * parcObject_Display(t, 0); + * + * parcObject_Release(&t); + * } + * @endcode + */ +void parcObject_Display(const PARCObject *object, const int indentation); + +/** + * Get the `PARCObjectDescriptor` of the given `PARCObject`. + * + * @param [in] object A pointer to a valid `PARCObject` instance + * + * @return A pointer to the given PARCObject's `PARCObjectDescriptor`. + * + * Example: + * @code + * { + * struct MyType *t = parcObject_Create(sizeof(struct MyType), &MyType_Descriptor); + * + * PARCObjectDescriptor *descriptor = parcObject_GetDescriptor(t); + * + * parcObject_Release(&t); + * } + * @endcode + */ +const PARCObjectDescriptor *parcObject_GetDescriptor(const PARCObject *object); + +/** + * Set the `PARCObjectDescriptor` of the given `PARCObject`. + * + * @param [in] object A pointer to a valid `PARCObject` instance + * @param [in] objectType A pointer to a valid `PARCObjectDescriptor` instance + * + * @return The previous value of the given PARCObject's `PARCObjectDescriptor`. + */ +const PARCObjectDescriptor *parcObject_SetDescriptor(PARCObject *object, const PARCObjectDescriptor *objectType); + +/** + * @def parcObject_MetaInitialize + * @deprecated Use parcObject_Override instead; + * + * Initialize a PARCObjectDescriptor structure. Every function pointer is set to NULL. + * + * @param [in] objectType A pointer to the PARCObjectDescriptor structure to initialize. + * + * @return The pointer to the initialized PARCObjectDescriptor. + */ +#define parcObject_MetaInitialize(objectType) \ + (objectType)->destructor = NULL, \ + (objectType)->destroy = NULL, \ + (objectType)->release = NULL, \ + (objectType)->copy = NULL, \ + (objectType)->toString = NULL, \ + (objectType)->equals = NULL, \ + (objectType)->compare = NULL, \ + (objectType)->hashCode = NULL, \ + (objectType)->toJSON = NULL, \ + (objectType)->display = NULL, \ + (objectType)->super = NULL + + +/** + * Create new `PARCObjectDescriptor` based on an existing `PARCObjectDescriptor.` + * The new `PARCObjectDescriptor` uses the existing `PARCObjectDescriptor` as the super-type of the new descriptor. + */ +#define parcObject_Extends(_subtype, _superType, ...) \ + LongBowCompiler_IgnoreInitializerOverrides \ + parcObjectDescriptor_Declaration(_subtype) = { \ + .super = &parcObject_DescriptorName(_superType), \ + .name = #_subtype, \ + .objectSize = 0, \ + .objectAlignment = 0, \ + .destroy = NULL, \ + .destructor = NULL, \ + .release = NULL, \ + .copy = NULL, \ + .toString = NULL, \ + .equals = NULL, \ + .compare = NULL, \ + .hashCode = NULL, \ + .toJSON = NULL, \ + .display = NULL, \ + .isLockable = true, \ + .typeState = NULL, \ + __VA_ARGS__ \ + }; \ + LongBowCompiler_WarnInitializerOverrides \ + const PARCObjectDescriptor parcObject_DescriptorName(_subtype) + +/** + * Define a new PARC Object implementation, by composing a new PARC Object Descriptor referencing an old one. + * The new PARC Object implementation must be accompanied by the corresponding `typedef` of the type containing the object's data. + */ +#define parcObject_Override(_subtype, _superType, ...) \ + parcObject_Extends(_subtype, _superType, \ + .objectSize = sizeof(_subtype), \ + .objectAlignment = sizeof(void *), \ + __VA_ARGS__) + + +/// Helper MACROS for PARCObject Normalization +/** \cond */ +/** + * parcObject_DestroyWrapper builds the boiler plate wrapper for PARCObject type conversion in the + * destroy operation. Intended for internal use. + */ +#define parcObject_DestroyWrapper(_type, _fname) \ + static void _autowrap_destroy_ ## _type(PARCObject **object) \ + { \ + _fname((_type **) object); \ + } + +/** + * parcObject_DestructorWrapper builds the boiler plate wrapper for PARCObject type conversion in the + * destroy operation. Intended for internal use. + */ +#define parcObject_DestructorWrapper(_type, _fname) \ + static bool _autowrap_destructor_ ## _type(PARCObject **object) \ + { \ + return _fname((_type **) object); \ + } + +/** + * `parcObject_CopyWrapper` builds the boiler plate wrapper for PARCObject type conversion in the + * copy operation. Intended for internal use. + */ +#define parcObject_CopyWrapper(_type, _fname) \ + static PARCObject *_autowrap_copy_ ## _type(const PARCObject *object) \ + { \ + return (PARCObject *) _fname((const _type *) object); \ + } + +/** + * parcObject_ToStringWrapper builds the boiler plate wrapper for PARCObject type conversion in the + * ToString operation. Intended for internal use. + */ +#define parcObject_ToStringWrapper(_type, _fname) \ + static char *_autowrap_toString_ ## _type(const PARCObject *object) \ + { \ + return _fname((const _type *) object); \ + } + +/** + * `parcObject_EqualsWrapper` builds the boiler plate wrapper for PARCObject type conversion in the + * equals operation. Intended for internal use. + */ +#define parcObject_EqualsWrapper(_type, _fname) \ + static bool _autowrap_equals_ ## _type(const PARCObject *a, const PARCObject *b) \ + { \ + return _fname((const _type *) a, (const _type *) b); \ + } + +/** + * parcObject_CompareWrapper builds the boiler plate wrapper for PARCObject type conversion in the + * compare operation. Intended for internal use. + */ +#define parcObject_CompareWrapper(_type, _fname) \ + static int _autowrap_compare_ ## _type(const PARCObject *a, const PARCObject *b) \ + { \ + return _fname((const _type *) a, (const _type *) b); \ + } + +/** + * `parcObject_HashCodeWrapper` builds the boiler plate wrapper for PARCObject type conversion in the + * HashCode operation. Intended for internal use. + */ +#define parcObject_HashCodeWrapper(_type, _fname) \ + static PARCHashCode _autowrap_hashCode_ ## _type(const PARCObject *object) \ + { \ + return _fname((const _type *) object); \ + } + +/** + * `parcObject_CopyWrapper` builds the boiler plate wrapper for PARCObject type conversion in the + * ToJSON operation. Intended for internal use. + */ +#define parcObject_ToJSONWrapper(_type, _fname) \ + static PARCJSON *_autowrap_toJSON_ ## _type(const PARCObject *object) \ + { \ + return _fname((const _type *) object); \ + } + +/** + * `parcObject_DisplayWrapper` builds the boiler plate wrapper for PARCObject type conversion in the + * Display operation. Intended for internal use. + */ +#define parcObject_DisplayWrapper(_type, _fname) \ + static void _autowrap_Display_ ## _type(const PARCObject *object, const int indentation) \ + { \ + _fname((const _type *) object, indentation); \ + } + +/** + * _autowrap_NULL is a part of the c-macro trick for implement a macro If-Else switch. + * If included as a macro parameter it inserts a comma into the parameter list for that macro. + * This can be used by a switching macro that always resolves to the nth element and the + * presence of a comma generating macro changes which element is the nth. When NULL is used + * as a parameter in a call to "ExtendPARCObject", _autowrap_NULL will be the name generated which + * expands to a comma. + */ +#define _autowrap_NULL(...) , + +/** \endcond */ + + +/** + * @define parcObject_ExtendPARCObject + * @deprecated Use parcObject_Override instead; + * + * @discussion parcObject_ExtendPARCObject is a helper macro for constructing a PARCObjectDescriptor Structure of + * function pointers pointing to a subtype's overriding functions. This struct serves the same + * purpose as a vTable in c++ and provides for simple polymorphism. The functions used as parameters + * should NOT call through to the parcObject function they override as this will result in an infinite loop. + * NULL should be used for functions where PARCObject's default implementation is desired. + * + * Note: It uses the parcCMacro_IfElse trickery to handle NULL parameters. + * + * @param [in] _destroy A pointer to the Destroy callback function. + * @param [in] _copy A pointer to the Copy callback function. + * @param [in] _toString A pointer to the ToString callback function. + * @param [in] _equals A pointer to the Equals callback function. + * @param [in] _compare A pointer to the Compare callback function. + * @param [in] _hashCode A pointer to the HashCode callback function. + * @param [in] _toJSON A pointer to the ToJSON callback function. + */ +#define parcObject_ExtendPARCObject(_type, _destroy, _copy, _toString, _equals, _compare, _hashCode, _toJSON) \ + parcCMacro_IfElse(, parcObject_DestroyWrapper(_type, _destroy), _autowrap_ ## _destroy()) \ + parcCMacro_IfElse(, parcObject_CopyWrapper(_type, _copy), _autowrap_ ## _copy()) \ + parcCMacro_IfElse(, parcObject_ToStringWrapper(_type, _toString), _autowrap_ ## _toString()) \ + parcCMacro_IfElse(, parcObject_EqualsWrapper(_type, _equals), _autowrap_ ## _equals()) \ + parcCMacro_IfElse(, parcObject_CompareWrapper(_type, _compare), _autowrap_ ## _compare()) \ + parcCMacro_IfElse(, parcObject_HashCodeWrapper(_type, _hashCode), _autowrap_ ## _hashCode()) \ + parcCMacro_IfElse(, parcObject_ToJSONWrapper(_type, _toJSON), _autowrap_ ## _toJSON()) \ + parcObject_Override(_type, PARCObject, \ + .destroy = parcCMacro_IfElse(NULL, _autowrap_destroy_ ## _type, _autowrap_ ## _destroy()), \ + .destructor = NULL, \ + .release = NULL, \ + .copy = parcCMacro_IfElse(NULL, _autowrap_copy_ ## _type, _autowrap_ ## _copy()), \ + .toString = parcCMacro_IfElse(NULL, _autowrap_toString_ ## _type, _autowrap_ ## _toString()), \ + .equals = parcCMacro_IfElse(NULL, _autowrap_equals_ ## _type, _autowrap_ ## _equals()), \ + .compare = parcCMacro_IfElse(NULL, _autowrap_compare_ ## _type, _autowrap_ ## _compare()), \ + .hashCode = parcCMacro_IfElse(NULL, _autowrap_hashCode_ ## _type, _autowrap_ ## _hashCode()), \ + .toJSON = parcCMacro_IfElse(NULL, _autowrap_toJSON_ ## _type, _autowrap_ ## _toJSON()), \ + .display = NULL) + +/** + * @define parcObject_CreateInstance + * + * `parcObject_CreateInstance` is a helper C-macro that creates an instance of a PARCObject subtype + * using `parcObject_CreateInstanceImpl` that is based on the PARCObjectDescriptor. + * + * @param [in] _subtype A subtype's type string (e.g. PARCBuffer) + */ +#define parcObject_CreateInstance(_subtype) \ + parcObject_CreateInstanceImpl(&parcObject_DescriptorName(_subtype)) + +PARCObject *parcObject_CreateInstanceImpl(const PARCObjectDescriptor *descriptor); + +/** + * @define parcObject_CreateAndClearInstance + * + * parcObject_CreateAndClearInstance is a helper C-macro that creates an instance of a PARCObject subtype + * using parcObject_CreateAndClear that is based on the PARCObjectDescriptor struct created by the + * parcObject_ExtendPARCObject macro. + * + * @param [in] _subtype A subtype's type string (e.g. PARCBuffer) + */ +#define parcObject_CreateAndClearInstance(_subtype) \ + (_subtype *) parcObject_CreateAndClearInstanceImpl(&parcObject_DescriptorName(_subtype)) + +/** + * Create a reference counted segment of memory of at least @p objectLength long. + * + * The implementation pointer, is either NULL or points to a valid `PARCObjectDescriptor` structure + * containing the callback functions that implement the object's life-cycle operations. + * + * The allocated memory is such that the memory's base address is aligned on a sizeof(void *) boundary, + * and filled with zero bytes. + * + * If memory cannot be allocated, `errno` is set to ENOMEM. + * + * @param [in] descriptor A pointer to a valid `PARCObjectDescriptor` structure. + * + * @return NULL The memory could not be allocated. + * @return non-NULL A pointer to reference counted memory of at least length bytes. + * + * Example: + * @code + * { + * struct timeval *t = parcObject_CreateAndClearInstanceImpl(sizeof(struct timeval), &PARCObject_Descriptor); + * } + * @endcode + * + * @see PARCObjectDescriptor + * @see parcObject_Create + */ +PARCObject *parcObject_CreateAndClearInstanceImpl(const PARCObjectDescriptor *descriptor); + +/** + * Define a static PARCObject instance for the given type, alignment, per-object data. + * + * Once the instance has been defined, it must be initialised via `parcObject_InitInstance` + * or `parcObject_InitAndClearInstance` before it is used. + * + * @return A pointer to an invalid `PARCObject` instance that must be initialised . + */ +#define parcObject_Instance(_type_, _alignment_, _size_) \ + (_type_ *) (& (char[parcObject_TotalSize(_alignment_, _size_)]) { 0 }[parcObject_PrefixLength(sizeof(void *))]) + +/** + * @define parcObject_InitInstance + * + * `parcObject_InitInstance` is a helper C-macro that initializes a portion of memory to contain a `PARCObject` subtype + * using `parcObject_InitInstanceImpl`. + * + * @param [in] _object_ A pointer to memory that will contain the object and its meta-data. + * @param [in] _subtype A subtype's type name. + */ +#define parcObject_InitInstance(_object_, _subtype) \ + parcObject_InitInstanceImpl(_object_, &parcObject_DescriptorName(_subtype)) + +/** + * @define parcObject_InitInstanceImpl + * + * Initialize a PARCObject instance given the `PARCObjectDescriptor`. + * Any previous state of the given PARCObject is destroyed. + * + * @param [in] object A pointer to an existing valid or invalid `PARCObject` instance. + * @param [in] descriptor A pointer to a valid `PARCObjectDescriptor` structure. + */ +PARCObject *parcObject_InitInstanceImpl(PARCObject *object, const PARCObjectDescriptor *descriptor); + +/** + * @define parcObject_InitAndClearInstance + * + * `parcObject_InitAndClearInstance` is a helper C-macro that initializes a portion of memory to contain a PARCObject subtype + * using `parcObject_InitAndClearInstanceImpl`. + * + * @param [in] _object_ A pointer to memory that will contain the object and its meta-data. + * @param [in] _subtype A subtype's type name. + */ +#define parcObject_InitAndClearInstance(_object_, _subtype) \ + parcObject_InitAndClearInstanceImpl(_object_, &parcObject_DescriptorName(_subtype)) + +/** + * Create a reference counted segment of memory of at least @p objectLength long. + * + * The implementation pointer, is either NULL or points to a valid `PARCObjectDescriptor` structure + * containing the callback functions that implement the object's life-cycle operations. + * + * The allocated memory is such that the memory's base address is aligned on a sizeof(void *) boundary, + * and filled with zero bytes. + * + * If memory cannot be allocated, `errno` is set to `ENOMEM`. + * + * @param [in] object A pointer to an existing valid or invalid `PARCObject` instance. + * @param [in] descriptor A pointer to a valid `PARCObjectDescriptor` structure. + * + * @return NULL The memory could not be allocated. + * @return non-NULL A pointer to reference counted memory of at least length bytes. + * + * Example: + * @code + * { + * + * } + * @endcode + * + * @see PARCObjectDescriptor + * @see parcObject_Create + */ +PARCObject *parcObject_InitAndClearInstanceImpl(PARCObject *object, const PARCObjectDescriptor *descriptor); + +/** + * Compute the number of bytes necessary for a PARC Object prefix. + * + * The @p _alignment_ parameter specifies the required memory alignment of the object. + * + * @param [in] _alignment_ An unsigned integer value greater than `sizeof(void *)` + * + * @return The number of bytes necessary for a PARC Object header. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +// The constant value here must be greater than or equal to the size of the internal _PARCObjectHeader structure. +#define parcObject_PrefixLength(_alignment_) ((152 + (_alignment_ - 1)) & - _alignment_) + +/** + * Compute the number of bytes necessary for a PARC Object. + * + * The number of bytes consists of the number of bytes for the PARC Object header, padding, and the object's specific data. + * + * The @p _alignment_ parameter specifies the required memory alignment of the object. + * The @p _size_ parameter specifies the number of bytes necessary for the object specific data. + */ +#define parcObject_TotalSize(_alignment_, _size_) (parcObject_PrefixLength(_alignment_) + _size_) + +/** + * Wrap a static, unallocated region of memory producing a valid pointer to a `PARCObject` instance. + * + * Note that the return value will not be equal to the value of @p origin. + * + * @param [in] memory A pointer to memory that will contain the object and its state. + * @param [in] descriptor The subtype name that will be used to compose the name of the `PARCObjectDescriptor` for the object. + * + * @return NULL An error occured. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +#define parcObject_Wrap(_memory_, _subtype) \ + parcObject_WrapImpl(_memory_, &parcObject_DescriptorName(_subtype)) + +/** + * Wrap a static, unallocated region of memory producing a valid pointer to a `PARCObject` instance. + * + * Note that the return value will not be equal to the value of @p origin. + * + * @param [in] memory A pointer to memory that will contain the object and its state. + * @param [in] descriptor A pointer to a valid `PARCObjectDescriptor` for the object. + * + * @return NULL An error occured. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +PARCObject *parcObject_WrapImpl(void *memory, const PARCObjectDescriptor *descriptor); + +/** + * @def parcObject_ImplementAcquire + * + * `parcObject_ImplementAcquire` is a helper C-macro that creates a canonical subtype specific + * Acquire function. + * + * @param [in] _namespace A subtype's namespace string (e.g. parcBuffer) + * @param [in] _type A subtype's type string (e.g. PARCBuffer) + */ +#define parcObject_ImplementAcquire(_namespace, _type) \ + _type * _namespace ## _Acquire(const _type * pObject) { \ + return (_type *) parcObject_Acquire((PARCObject *) pObject); \ + } extern _type *_namespace ## _Acquire(const _type * pObject) + +/** + * @def parcObject_ImplementRelease + * + * `parcObject_ImplementRelease` is a helper C-macro that creates a canonical subtype specific + * Release function. + * + * @param [in] _namespace A subtype's namespace string (e.g. parcBuffer) + * @param [in] _type A subtype's type string (e.g. PARCBuffer) + */ +#define parcObject_ImplementRelease(_namespace, _type) \ + inline void _namespace ## _Release(_type **pObject) { \ + parcObject_Release((PARCObject **) pObject); \ + } extern void _namespace ## _Release(_type **pObject) + +/** + * `parcObject_ImplementationCheck` is a helper macro that will generate compile time warnings for + * missing canonical functions or canonical functions with faulty signatures. + * + * @param _namespace A subtype's namespace string (e.g. parcBuffer) + * @param _type A subtype's type string (e.g. PARCBuffer) + */ +#define parcObject_ImplementationCheck(_namespace, _type) \ + static void \ + _impl_check() { \ + _type *po; \ + const _type co; \ + const _type *pco; \ + pco = _namespace ## _Copy(&co); \ + pco = _namespace ## _Acquire(&co); \ + pco = pco; \ + _namespace ## _Release(&po); \ + bool b = _namespace ## _Equals(&co, &co); \ + b = b; \ + int i = _namespace ## _Compare(&co, &co); \ + i = i; \ + char *pc = _namespace ## _ToString(&co); \ + pc = pc; \ + uint32_t ui = _namespace ## _HashCode(&co); \ + ui = ui; \ + PARCJSON *pj = _namespace ## _ToJSON(&co); \ + pj = pj; \ + } typedef void parcCMacro_Cat (_type, _IC_NOOP) + +/** + * Obtain the lock on the given `PARCObject` instance. + * + * If the lock is already held by another thread, this function will block. + * If the lock is aleady held by the current thread, this function will return `false`. + * + * Implementors must avoid deadlock by attempting to lock the object a second time within the same calling thread. + * + * @param [in] object A pointer to a valid `PARCObject` instance. + * + * @return true The lock was obtained successfully. + * @return false The lock is already held by the current thread, the `PARCObject` is invalid, or does not support locking. + * + * Example: + * @code + * { + * if (parcObject_Lock(object)) { + * + * } + * } + * @endcode + */ +bool parcObject_Lock(const PARCObject *object); + +/** + * @def parcObject_ImplementLock + * + * `parcObject_ImplementLock` is a helper C-macro that defines a static, inline facade for the `parcObject_Lock` function. + * + * @param [in] _namespace A subtype's namespace string (e.g. `parcBuffer`) + * @param [in] _type A subtype's type string (e.g. `PARCBuffer`) + */ +#define parcObject_ImplementLock(_namespace, _type) \ + static inline bool _namespace ## _Lock(const _type * pObject) { \ + return parcObject_Lock((PARCObject *) pObject); \ + } typedef void parcCMacro_Cat (_type, _Lock_NOOP) + +/** + * Try to obtain the advisory lock on the given `PARCObject` instance. + * + * Once the lock is obtained, the caller must release the lock via `parcObject_Unlock`. + * + * @param [in] object A pointer to a valid `PARCObject` instance. + * + * @return true The `PARCObject` is locked. + * @return false The `PARCObject` is unlocked, or does not support locking. + * + * Example: + * @code + * { + * while (parcObject_TryLock(object)) + * ; + * } + * @endcode + */ +bool parcObject_TryLock(const PARCObject *object); + +/** + * @def parcObject_ImplementTryLock + * + * `parcObject_ImplementTryLock` is a helper C-macro that defines a static, inline facade for the `parcObject_TryLock` function. + * + * @param [in] _namespace A subtype's namespace string (e.g. `parcBuffer`) + * @param [in] _type A subtype's type string (e.g. `PARCBuffer`) + */ +#define parcObject_ImplementTryLock(_namespace, _type) \ + static inline bool _namespace ## _TryLock(const _type * pObject) { \ + return parcObject_TryLock((PARCObject *) pObject); \ + } typedef void parcCMacro_Cat (_type, _TryLock_NOOP) + +/** + * Try to unlock the advisory lock on the given `PARCObject` instance. + * + * @param [in] object A pointer to a valid `PARCObject` instance. + * + * @return true The `PARCObject` was locked and now is unlocked. + * @return false The `PARCObject` was not locked and remains unlocked. + * + * Example: + * @code + * { + * if (parcObject_Lock(Object)) { + * parcObject_Unlock(object); + * } + * } + * @endcode + */ +bool parcObject_Unlock(const PARCObject *object); + +/** + * @def parcObject_ImplementUnlock + * + * `parcObject_ImplementUnlock` is a helper C-macro that defines a static, inline facade for the `parcObject_Unlock` function. + * + * @param [in] _namespace A subtype's namespace string (e.g. `parcBuffer`) + * @param [in] _type A subtype's type string (e.g. `PARCBuffer`) + */ +#define parcObject_ImplementUnlock(_namespace, _type) \ + static inline bool _namespace ## _Unlock(const _type * pObject) { \ + return parcObject_Unlock((PARCObject *) pObject); \ + } typedef void parcCMacro_Cat (_type, _Unlock_NOOP) + +/** + * Determine if the advisory lock on the given PARCObject instance is locked. + * + * @param [in] object A pointer to a valid PARCObject instance. + * + * @return true The `PARCObject` is locked. + * @return false The `PARCObject` is unlocked. + * Example: + * @code + * { + * if (parcObject_Lock(object)) { + * ... + * if (parcObject_IsLocked(object) { + * .... + * } + * ... + * parcObject_Unlock(object); + * } + * } + * @endcode + */ +bool parcObject_IsLocked(const PARCObject *object); + +/** + * @def parcObject_ImplementIsLocked + * + * parcObject_ImplementIsLocked is a helper C-macro that defines a static, inline facade for the `parcObject_IsLocked` function. + * + * @param [in] _namespace A subtype's namespace string (e.g. `parcBuffer`) + * @param [in] _type A subtype's type string (e.g. `PARCBuffer`) + */ +#define parcObject_ImplementIsLocked(_namespace, _type) \ + static inline bool _namespace ## _IsLocked(const _type * pObject) { \ + return parcObject_IsLocked((const PARCObject *) pObject); \ + } typedef void parcCMacro_Cat (_type, _IsLocked_NOOP) + +/** + * Causes the calling thread to wait until another thread invokes the `parcObject_Notify()` function on the same object. + * + * The calling thread must own this object's lock. + * The calling thread will release ownership of this lock and wait until another thread invokes `parcObject_Notify` + * on the same object. The original calling thread then re-obtains ownership of the lock and resumes execution. + * + * This function must only be called by a thread that is the owner of this object's lock. + * + * @param [in] object A pointer to a valid PARCObject instance. + * + * Example: + * @code + * { + * if (parcObject_Lock(object)) { + * ... + * if (parcObject_Wait(object) { + * .... + * } + * ... + * parcObject_Unlock(object); + * } + * } + * @endcode + */ +void parcObject_Wait(const PARCObject *object); + +/** + * @def parcObject_ImplementWait + * + * parcObject_ImplementWait is a helper C-macro that defines a static, inline facade for the `parcObject_Wait` function. + * + * @param [in] _namespace A subtype's namespace string (e.g. `parcBuffer`) + * @param [in] _type A subtype's type string (e.g. `PARCBuffer`) + * @see parcObject_Wait + */ +#define parcObject_ImplementWait(_namespace, _type) \ + static inline void _namespace ## _Wait(const _type * pObject) { \ + parcObject_Wait((const PARCObject *) pObject); \ + } typedef void parcCMacro_Cat (_type, _Wait_NOOP) + + +/** + * Causes the calling thread to wait until either another thread invokes the `parcObject_Notify()` + * function on the same object or the system time equals or exceeds the specified time. + * + * The calling thread must own the object's lock. + * The calling thread will release ownership of this lock and wait until another thread invokes + * `parcObject_Notify` or the computer's system time equals or exceeds that specified by @p time. + * on the same object. + * The original calling thread then re-obtains ownership of the lock and resumes execution. + * + * This function must only be called by a thread that is the owner of this object's lock. + * + * @param [in] object A pointer to a valid PARCObject instance. + * + * @returns false if the alloted time was exceeded. + * @returns true if another thread invoked the `parcObject_Notify()` function + * + * Example: + * @code + * { + * struct timeval tv; + * gettimeofday(&tv, NULL); + * + * struct timespec absoluteTime; + * absoluteTime.tv_sec = tv.tv_sec + 0; + * absoluteTime.tv_nsec = 0; + * + * parcObject_WaitUntil(object, &absoluteTime); + * } + * @endcode + */ +bool parcObject_WaitUntil(const PARCObject *object, const struct timespec *time); + +/** + * @def parcObject_ImplementWaitUntil + * + * parcObject_ImplementWaitUntil is a helper C-macro that defines a static, inline facade for the + * `parcObject_WaitUntil` function. + * + * @param [in] _namespace A subtype's namespace string (e.g. `parcBuffer`) + * @param [in] _type A subtype's type string (e.g. `PARCBuffer`) + * @see parcObject_WaitUntil + */ +#define parcObject_ImplementWaitUntil(_namespace, _type) \ + static inline bool _namespace ## _WaitUntil(const _type * pObject, const struct timespec *time) { \ + return parcObject_WaitUntil((const PARCObject *) pObject, time); \ + } typedef void parcCMacro_Cat (_type, _WaitUntil_NOOP) + +/** + * Causes the calling thread to wait until either another thread invokes the `parcObject_Notify()` + * function on the same object or the given number of nanoseconds elapses. + * + * The calling thread must own the object's lock. + * + * The calling thread will release ownership of its lock and wait until another thread invokes + * `parcObject_Notify` on the same object, + * or the computer's system time equals or exceeds the time specified by the + * time of invocation plus nanoSeconds. + * The original calling thread then re-obtains ownership of the lock and resumes execution. + * + * This function must only be called by a thread that is the owner of this object's lock. + * + * @param [in] object A pointer to a valid PARCObject instance. + * @param [in] nanoSeconds The number of nanoseconds to wait. + * + * @returns false if the allotted time was exceeded. + * @returns true if another thread invoked the `parcObject_Notify()` function + * + * Example: + * @code + * { + * parcObject_WaitFor(object, 1000000000UL); + * } + * @endcode + */ +bool parcObject_WaitFor(const PARCObject *object, const uint64_t nanoSeconds); + +/** + * @def parcObject_ImplementWaitUntil + * + * parcObject_ImplementWaitUntil is a helper C-macro that defines a static, inline facade for the + * `parcObject_WaitUntil` function. + * + * @param [in] _namespace A subtype's namespace string (e.g. `parcBuffer`) + * @param [in] _type A subtype's type string (e.g. `PARCBuffer`) + * @see parcObject_WaitUntil + */ +#define parcObject_ImplementWaitFor(_namespace, _type) \ + static inline bool _namespace ## _WaitFor(const _type * pObject, const unsigned long nanoSeconds) { \ + return parcObject_WaitFor((const PARCObject *) pObject, nanoSeconds); \ + } typedef void parcCMacro_Cat (_type, _WaitFor_NOOP) + +/** + * Wakes up a single thread that is waiting on this object (see `parcObject_Wait)`. + * If any threads are waiting on this object, one of them is chosen to be awakened. + * The choice is arbitrary and occurs at the discretion of the underlying implementation. + * + * The awakened thread will not be able to proceed until the current thread relinquishes the lock on this object. + * The awakened thread will compete in the usual manner with any other threads that might be actively + * competing to synchronize on this object; + * for example, + * the awakened thread enjoys no reliable privilege or disadvantage in being the next thread to lock this object. + * + * @param [in] object A pointer to a valid `PARCObject` instance. + * + * Example: + * @code + * { + * if (parcObject_Lock(object)) { + * parcObject_Notify(object); + * parcObject_Unlock(object); + * } + * } + * @endcode + */ +void parcObject_Notify(const PARCObject *object); + +/** + * @def parcObject_ImplementNotify + * + * parcObject_ImplementNotify is a helper C-macro that defines a static, inline facade for the `parcObject_Notify` function. + * + * @param [in] _namespace A subtype's namespace string (e.g. `parcBuffer`) + * @param [in] _type A subtype's type string (e.g. `PARCBuffer`) + */ +#define parcObject_ImplementNotify(_namespace, _type) \ + static inline void _namespace ## _Notify(const _type * pObject) { \ + parcObject_Notify((const PARCObject *) pObject); \ + } typedef void parcCMacro_Cat (_type, _Notify_NOOP) + + +/** + * Wakes up all threads that are waiting on the given object's lock. + * + * A thread waits on an object by calling one of the wait methods, `parcObject_Wait`, `parcObject_WaitFor`, `parcObject_WaitUntil`. + * The awakened threads will proceed after the current thread relinquishes the lock on the given object. + * The awakened threads will compete in the usual manner with any other threads that might be actively competing + * to synchronize on this object. + * Awakened threads have no priority between them in being the next thread to lock this object. + * + * This method can only be called by a thread that is the owner of this object's lock. + * + * @param [in] object A pointer to a valid `PARCObject` instance. + * + * Example: + * @code + * { + * if (parcObject_Lock(object)) { + * parcObject_NotifyAll(object); + * parcObject_Unlock(object); + * } + * } + * @endcode + */ +void parcObject_NotifyAll(const PARCObject *object); + +/** + * @def parcObject_ImplementNotifyAll + * + * parcObject_ImplementNotifyAll is a helper C-macro that defines a static, inline facade for the `parcObject_NotifyAll` function. + * + * @param [in] _namespace A subtype's namespace string (e.g. `parcBuffer`) + * @param [in] _type A subtype's type (e.g. `PARCBuffer`) + */ +#define parcObject_ImplementNotifyAll(_namespace, _type) \ + static inline void _namespace ## _NotifyAll(const _type * pObject) { \ + parcObject_NotifyAll((const PARCObject *) pObject); \ + } typedef void parcCMacro_Cat (_type, _NotifyAll_NOOP) + +/** + * @def parcObject_Mutex + * + * This macro uses the functions `parcObject_Lock` and `parcObject_Unlock` + * to provide a simple syntax for implementing a mutual exclusion region of code. + * + * @param [in] _object_ A pointer to a valid PARCObject that implements locking. + * + * Example: + * @code + * { + * parcObject_Mutex(object) { + * .... + * } + * } + * @endcode + * @see parcObject_Synchronize + */ +#define parcObject_Mutex(_object_) for (bool once = true; once && parcObject_Lock(_object_); parcObject_Unlock(_object_), once = false) + +/** + * Determine if a given `PARCObject` is and instance of the specified `PARCObjectDescriptor`. + * + * @param [in] object A pointer to a valid PARCObject instance. + * @param [in] descriptor A pointer to a valid PARCObjectDescriptor instance. + * + * @return true @p object is an instance of @p descriptor. + * @return false @p object is not an instance of @p descriptor. + */ +bool parcObject_IsInstanceOf(const PARCObject *object, const PARCObjectDescriptor *descriptor); + +/** + * Atomically set an object's barrier. + * + * If the barrier is not set, the barrier will be set and this function returns `true`. + * + * If the barrier is already set, any subsequent attempt to set the barrier will block until the barrier is unset + * (see `parcObject_BarrierUnset`). + * If there are multiple competitors to set the barrier, + * only one will (indiscriminately) succeed and return and the remaining will continue to attempt to set the barrier. + * + * Barriers can be used in both threaded and non-threaded applications, + * but are not a substitute for thread locking and do not interoperate the wait and notify operations. + * + * Barriers should be used in pairs within the same level of program abstraction to avoid confusion. + * It is possible to set a barrier without ever unsetting the same barrier, + * and as a consequence any other attempt to set the same barrier will hang the program. + * + * @param [in] object A pointer to a valid PARCObject + * + * @return true + * + * Example: + * @code + * { + * parcObject_BarrierSet(object); + * + * ... + * + * parcObject_BarrierUnset(object); + * } + * @endcode + * @see parcObject_BarrierUnset + * @see parcObject_Synchronize + */ +bool parcObject_BarrierSet(const PARCObject *object); + +/** + * Unset an objects' barrier. + * + * If a barrier is set (see `parcObject_BarrierSet`), the barrier is unset (see `parcObject_BarrierUnset`). + * + * If a barrier is not set, this function will block until the barrier is set, + * whereupon it will be immediately unset the barrier and return. + * + * If there are multiple competitors attempting to unset the barrier, + * only one will (indiscriminately) succeed and return and the remaining will continue to attempt to unset the barrier. + * + * Barriers are not a substitute for thread locking and do not interoperate the wait and notify operations. + * + * + * @param [in] object A pointer to a valid `PARCObject` + * + * @return false + * + * Example: + * @code + * { + * parcObject_BarrierSet(object); + * + * ... + * + * parcObject_BarrierUnset(object); + * } + * @endcode + */ +bool parcObject_BarrierUnset(const PARCObject *object); + +/** + * Synchronize on a `PARCObject` instance to provide a simple mutual exclusion region of code. + * + * This macro uses the functions `parcObject_BarrierSet` and `parcObject_BarrierUnset` + * to provide a simple syntax for implementing a mutual exclusion region of code. + * + * This defines and uses the local variable `_parcObjectSynchronize` which will always appear to be true to the calling function + * and must never be defined by the calling function, or any module. + * + * @param [in] object A pointer to a valid `PARCObject` + * + * Example: + * @code + * { + * parcObject_Synchronize(_object_) { + * // Only one thread executes this code at a single time. + * } + * } + * @endcode + * @see parcObject_Mutex + */ +#define parcObject_Synchronize(_object_) for (bool _parcObjectSynchronize = parcObject_BarrierSet(_object_); _parcObjectSynchronize; _parcObjectSynchronize = parcObject_BarrierUnset(_object_)) + +#endif // libparc_parc_Object_h diff --git a/libparc/parc/algol/parc_OldSortedList.c b/libparc/parc/algol/parc_OldSortedList.c new file mode 100755 index 00000000..ef007388 --- /dev/null +++ b/libparc/parc/algol/parc_OldSortedList.c @@ -0,0 +1,101 @@ +/* + * 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 <LongBow/runtime.h> + +#include <parc/algol/parc_SortedList.h> + +#include <parc/algol/parc_ArrayList.h> +#include <parc/algol/parc_Memory.h> + +struct parc_sorted_list { + parcSortedList_Compare compare; + PARCArrayList *arrayList; +}; + +PARCSortedList * +parcSortedList_Create(parcSortedList_Compare compareFunction) +{ + PARCSortedList *sortedList = parcMemory_Allocate(sizeof(PARCSortedList)); + assertNotNull(sortedList, "parcMemory_Allocate(%zu) returned NULL", sizeof(PARCSortedList)); + sortedList->arrayList = parcArrayList_Create(NULL); + sortedList->compare = compareFunction; + return sortedList; +} + +void +parcSortedList_Destroy(PARCSortedList **parcSortedListPointer) +{ + parcArrayList_Destroy(&((*parcSortedListPointer)->arrayList)); + parcMemory_Deallocate((void **) parcSortedListPointer); + *parcSortedListPointer = NULL; +} + +void +parcSortedList_Add(PARCSortedList *parcSortedList, void *newItem) +{ + assertNotNull(parcSortedList, "sortedList parameter can't be null"); + assertNotNull(parcSortedList->arrayList, "arrayList can't be null"); + assertNotNull(newItem, "newItem can't be null"); + + size_t total_items = parcArrayList_Size(parcSortedList->arrayList); + for (size_t i = 0; i < total_items; i++) { + void *oldItem = parcArrayList_Get(parcSortedList->arrayList, i); + if (parcSortedList->compare(newItem, oldItem) == -1) { + // The old item at position i is bigger than the new item, + // we must insert the newItem here. + parcArrayList_InsertAtIndex(parcSortedList->arrayList, newItem, i); + return; + } + } + // We reached the end of the list, it must go here... + parcArrayList_Add(parcSortedList->arrayList, newItem); +} + +size_t +parcSortedList_Length(PARCSortedList *parcSortedList) +{ + return parcArrayList_Size(parcSortedList->arrayList); +} + +void * +parcSortedList_PopFirst(PARCSortedList *parcSortedList) +{ + assertNotNull(parcSortedList, "sortedList parameter can't be null"); + assertNotNull(parcSortedList->arrayList, "arrayList can't be null"); + + if (parcArrayList_Size(parcSortedList->arrayList) == 0) { + return NULL; + } + void *item = parcArrayList_Get(parcSortedList->arrayList, 0); + parcArrayList_RemoveAndDestroyAtIndex(parcSortedList->arrayList, 0); + return item; +} + +void * +parcSortedList_GetFirst(PARCSortedList *parcSortedList) +{ + assertNotNull(parcSortedList, "sortedList parameter can't be null"); + assertNotNull(parcSortedList->arrayList, "arrayList can't be null"); + + if (parcArrayList_Size(parcSortedList->arrayList) == 0) { + return NULL; + } + return parcArrayList_Get(parcSortedList->arrayList, 0); +} diff --git a/libparc/parc/algol/parc_OldSortedList.h b/libparc/parc/algol/parc_OldSortedList.h new file mode 100755 index 00000000..2a00a0fa --- /dev/null +++ b/libparc/parc/algol/parc_OldSortedList.h @@ -0,0 +1,122 @@ +/* + * 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. + */ + +/** + * @file parc_SortedList.h + * @ingroup datastructures + * @brief A sorted list + * + */ +#ifndef libparc_parc_SortedList_h +#define libparc_parc_SortedList_h + +#include <stdlib.h> + +struct parc_sorted_list; + +typedef struct parc_sorted_list PARCSortedList; + +/** + * This is a compare function that must be defined for the sorted list to sort + * + * @param [in] object1 The first object to compare. + * @param [in] object2 The second object to compare. + * @return -1 if object1 < object2, 0 if object1 == object2, 1 if object1 > object2. + * + * Example: + * @code + * <#example#> + * @endcode + */ +typedef int (*parcSortedList_Compare)(const void *object1, const void *object2); + +/** + * Create a sorted list + * This list will be sorted from smallest to largest based on the compare function. + * + * @param [in] compareFunction A compare function to determine how elements are sorted. + * @return An allocated `PARCSortedList`. + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCSortedList *parcSortedList_Create(parcSortedList_Compare compareFunction); + +/** + * Destroy an allocated sorted list + * + * @param [in,out] parcSortedListPointer A pointer to the allocated sorted list to destroy. The pointer will be set to NULL. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void parcSortedList_Destroy(PARCSortedList **parcSortedListPointer); + +/** + * Add an element to the sorted list. + * + * @param [in,out] parcSortedList A pointer to the `PARCSortedList` to modify. + * @param newItem The new item to add to the list. This item must be comparable by the compare function. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void parcSortedList_Add(PARCSortedList *parcSortedList, void *newItem); + +/** + * Return the length of the list + * + * @param [in] parcSortedList a pointer to an allocated sorted list. + * @return return the length of the list, the number of elements. + * + * Example: + * @code + * <#example#> + * @endcode + */ +size_t parcSortedList_Length(PARCSortedList *parcSortedList); + +/** + * Pop the first element on the sorted list. This will remove the element from the list and return it to the caller. + * + * @param [in,out] parcSortedList A pointer to the `PARCSortedList` to modify. + * @return The first element of @p parcSortedList. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void *parcSortedList_PopFirst(PARCSortedList *parcSortedList); + +/** + * Get the first element on the sorted list. This will NOT remove the element from the list. + * + * @param [in] parcSortedList A pointer to the `PARCSortedList` . + * @return The first element of the sorted list. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void *parcSortedList_GetFirst(PARCSortedList *parcSortedList); +#endif // libparc_parc_SortedList_h diff --git a/libparc/parc/algol/parc_OutputStream.c b/libparc/parc/algol/parc_OutputStream.c new file mode 100755 index 00000000..d00c1f09 --- /dev/null +++ b/libparc/parc/algol/parc_OutputStream.c @@ -0,0 +1,81 @@ +/* + * 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 <stdio.h> +#include <stdarg.h> + +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_OutputStream.h> + +struct parc_output_stream { + void *instance; + const PARCOutputStreamInterface *interface; +}; + +static void +_destroy(PARCOutputStream **streamPtr) +{ + PARCOutputStream *stream = *streamPtr; + (stream->interface->Release)((PARCOutputStream **) &stream->instance); +} + +parcObject_ExtendPARCObject(PARCOutputStream, _destroy, NULL, NULL, NULL, NULL, NULL, NULL); + +PARCOutputStream * +parcOutputStream_Create(void *instance, const PARCOutputStreamInterface *interface) +{ + PARCOutputStream *result = parcObject_CreateInstance(PARCOutputStream); + result->instance = instance; + result->interface = interface; + + return result; +} + +parcObject_ImplementAcquire(parcOutputStream, PARCOutputStream); + +parcObject_ImplementRelease(parcOutputStream, PARCOutputStream); + +size_t +parcOutputStream_Write(PARCOutputStream *stream, PARCBuffer *buffer) +{ + return (stream->interface->Write)(stream->instance, buffer); +} + +size_t +parcOutputStream_WriteCStrings(PARCOutputStream *stream, ...) +{ + va_list ap; + va_start(ap, stream); + + size_t result = 0; + + for (char *string = va_arg(ap, char *); string != NULL; string = va_arg(ap, char *)) { + result += parcOutputStream_WriteCString(stream, string); + } + + return result; +} + +size_t +parcOutputStream_WriteCString(PARCOutputStream *stream, const char *string) +{ + PARCBuffer *buffer = parcBuffer_WrapCString((char *) string); + size_t result = (stream->interface->Write)(stream->instance, buffer); + parcBuffer_Release(&buffer); + return result; +} diff --git a/libparc/parc/algol/parc_OutputStream.h b/libparc/parc/algol/parc_OutputStream.h new file mode 100755 index 00000000..db0ba343 --- /dev/null +++ b/libparc/parc/algol/parc_OutputStream.h @@ -0,0 +1,190 @@ +/* + * 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. + */ + +/** + * @file parc_OutputStream.h + * @ingroup inputoutput + * @brief A polymophic interface to specific implementations of modules that implement the + * output stream capabilities. + * + * + * + */ +#ifndef libparc_parc_OutputStream_h +#define libparc_parc_OutputStream_h + +#include <parc/algol/parc_Buffer.h> + +struct parc_output_stream; +typedef struct parc_output_stream PARCOutputStream; + +typedef struct parc_output_stream_interface { + size_t (*Write)(PARCOutputStream *stream, PARCBuffer *buffer); + + PARCOutputStream *(*Acquire)(PARCOutputStream * stream); + + void (*Release)(PARCOutputStream **streamPtr); +} PARCOutputStreamInterface; + +/** + * Create an valid PARCOutputStream instance from the given pointers to a properly + * initialized `PARCOutputStreamInterface` + * and specific instance structure that will be supplied to the underlying interface. + * + * @param [in] instance A pointer to a `PARCObject` that will be the parameter to the functions specifed by @p interface. + * @param [in] interface A pointer to a `PARCOutputStreamInterface`. + * + * @return non-NULL A pointer to a valid PARCOutputStream instance. + * @return NULL An error occurred. + * + * Example: + * @code + * { + * PARCFileOutputStream *fileOutput = parcFileOutputStream_Create(1); + * PARCOutputStream *output = parcOutputStream_Create(parcFileOutputStream_Acquire(fileOutputStream), + * PARCFileOutputStreamAsPARCInputStream); + * } + * @endcode + */ +PARCOutputStream *parcOutputStream_Create(void *instance, const PARCOutputStreamInterface *interface); + +/** + * Write the contents of the given `PARCBuffer` to the output stream. + * + * The position of the `PARCBuffer` will be set to its limit as a side-effect. + * + * @param [in] stream A pointer to a valid `PARCOutputStream` instance. + * @param [in] buffer A pointer to the `PARCBuffer` whose contents should be written to @p stream. + * + * @return The number of bytes written + * + * Example: + * @code + * { + * PARCFileOutputStream *fileOutput = parcFileOutputStream_Create(1); + * PARCOutputStream *output = parcOutputStream_Create(parcFileOutputStream_Acquire(fileOutputStream), + * PARCFileOutputStreamAsPARCInputStream); + * PARCBuffer *buffer = parcBuffer_WrapCString("Hello World"); + * parcOutputStream_Write(output, buffer); + * parcOutputStream_Release(&output); + * } + * @endcode + */ +size_t parcOutputStream_Write(PARCOutputStream *stream, PARCBuffer *buffer); + +/** + * Write a nul-terminated C string to the given `PARCOutputStream`. + * + * @param [in] stream A pointer to a valid `PARCOutputStream` instance. + * @param [in] string A nul-terminated C string. + * + * @return The number of bytes written. + * + * Example: + * @code + * { + * PARCFileOutputStream *fileOutput = parcFileOutputStream_Create(1); + * PARCOutputStream *output = parcOutputStream_Create(parcFileOutputStream_Acquire(fileOutputStream), + * PARCFileOutputStreamAsPARCInputStream); + * + * parcOutputStream_WriteCStrings(output, "Hello", " ", "World", NULL); + * + * parcOutputStream_Release(&output); + * } + * @endcode + */ +size_t parcOutputStream_WriteCString(PARCOutputStream *stream, const char *string); + +/** + * Write one or more nul-terminated C strings to the given `PARCOutputStream`. + * + * @param [in] stream A pointer to a valid `PARCOutputStream` instance. + * @param [in] ... A NULL terminated variable argument list of nul-terminated C strings. + * + * @return The number of bytes written. + * + * Example: + * @code + * { + * PARCFileOutputStream *fileOutput = parcFileOutputStream_Create(1); + * PARCOutputStream *output = parcOutputStream_Create(parcFileOutputStream_Acquire(fileOutputStream), + * PARCFileOutputStreamAsPARCInputStream); + * + * parcOutputStream_WriteCStrings(output, "Hello", " ", "World", NULL); + * + * parcOutputStream_Release(&output); + * } + * @endcode + */ +size_t parcOutputStream_WriteCStrings(PARCOutputStream *stream, ...); + +/** + * Increase the number of references to a `PARCOutputStream`. + * + * Note that new `PARCOutputStream` is not created, + * only that the given `PARCOutputStream` reference count is incremented. + * Discard the reference by invoking `parcOutputStream_Release`. + * + * @param [in] stream A pointer to a `PARCOutputStream` instance. + * + * @return The input `PARCOutputStream` pointer. + * + * Example: + * @code + * { + * PARCFileOutputStream *fileOutput = parcFileOutputStream_Create(1); + * PARCOutputStream *output = parcOutputStream_Create(parcFileOutputStream_Acquire(fileOutputStream), + * PARCFileOutputStreamAsPARCInputStream); + * PARCBuffer *buffer = parcBuffer_WrapCString("Hello World"); + * parcOutputStream_Write(output, buffer); + * + * PARCOutputStream *x = parcOutputStream_Acquire(output); + * + * parcBuffer_Release(&x); + * parcOutputStream_Release(&output); + * } + * @endcode + * @see parcOutputStream_Release + */ +PARCOutputStream *parcOutputStream_Acquire(const PARCOutputStream *stream); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's interface will perform + * additional cleanup and release other privately held references. + * + * @param [in] streamPtr A pointer to a pointer to the instance to release. + * + * Example: + * @code + * { + * PARCFileOutputStream *fileOutput = parcFileOutputStream_Create(1); + * PARCOutputStream *output = parcOutputStream_Create(parcFileOutputStream_Acquire(fileOutputStream), + * PARCFileOutputStreamAsPARCInputStream); + * PARCBuffer *buffer = parcBuffer_WrapCString("Hello World"); + * parcOutputStream_Write(output, buffer); + * parcOutputStream_Release(&output); + * } + * @endcode + * @see parcOutputStream_Acquire + * @see parcOutputStream_Create + */ +void parcOutputStream_Release(PARCOutputStream **streamPtr); +#endif // libparc_parc_OutputStream_h diff --git a/libparc/parc/algol/parc_PathName.c b/libparc/parc/algol/parc_PathName.c new file mode 100755 index 00000000..e329ee88 --- /dev/null +++ b/libparc/parc/algol/parc_PathName.c @@ -0,0 +1,277 @@ +/* + * 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 <LongBow/runtime.h> + +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <parc/algol/parc_Deque.h> + +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_DisplayIndented.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_BufferComposer.h> + +#include <parc/algol/parc_PathName.h> + +struct parc_pathname { + bool isAbsolute; + PARCDeque *path; +}; + +static void +_destroy(PARCPathName **pathNamePtr) +{ + PARCPathName *pathName = *pathNamePtr; + + for (size_t i = 0; i < parcDeque_Size(pathName->path); i++) { + void *name = parcDeque_GetAtIndex(pathName->path, i); + parcMemory_Deallocate((void **) &name); + } + parcDeque_Release(&pathName->path); +} + +static bool +_pathNameSegmentEquals(const void *x, const void *y) +{ + if (x == y) { + return true; + } + if (x == NULL || y == NULL) { + return false; + } + return strcmp((char *) x, (char *) y) == 0; +} + +static void * +_pathNameSegmentCopy(const void *x) +{ + return parcMemory_StringDuplicate(x, strlen(x)); +} + +static const PARCObjectDescriptor parcPathNameSegment_ObjectInterface = { + .destroy = (PARCObjectDestroy *) NULL, + .copy = (PARCObjectCopy *) _pathNameSegmentCopy, + .toString = (PARCObjectToString *) NULL, + .equals = (PARCObjectEquals *) _pathNameSegmentEquals, + .compare = (PARCObjectCompare *) NULL +}; + +parcObject_ExtendPARCObject(PARCPathName, _destroy, parcPathName_Copy, parcPathName_ToString, + parcPathName_Equals, NULL, NULL, NULL); + +PARCPathName * +parcPathName_ParseToLimit(size_t limit, const char path[limit]) +{ + PARCPathName *result = parcPathName_Create(); + + if (limit > 0) { + size_t index = 0; + + if (path[index] == '/') { + result->isAbsolute = true; + index++; + } + while (path[index] != 0 && index < limit) { + while (path[index] == '/' && index < limit) { + index++; + } + if (path[index] != 0 && index < limit) { + size_t segment = index; + while (path[index] != 0 && path[index] != '/' && index < limit) { + index++; + } + + parcDeque_Append(result->path, parcMemory_StringDuplicate(&path[segment], index - segment)); + } + } + } + + return result; +} + +PARCPathName * +parcPathName_Parse(const char *path) +{ + return parcPathName_ParseToLimit(strlen(path), path); +} + +PARCPathName * +parcPathName_Create(void) +{ + PARCPathName *result = parcObject_CreateInstance(PARCPathName); + + result->isAbsolute = false; + result->path = parcDeque_CreateObjectInterface(&parcPathNameSegment_ObjectInterface); + return result; +} + +parcObject_ImplementAcquire(parcPathName, PARCPathName); + +parcObject_ImplementRelease(parcPathName, PARCPathName); + +PARCPathName * +parcPathName_Copy(const PARCPathName *pathName) +{ + PARCPathName *result = parcObject_CreateInstance(PARCPathName); + + result->isAbsolute = pathName->isAbsolute; + result->path = parcDeque_Copy(pathName->path); + return result; +} + +bool +parcPathName_Equals(const PARCPathName *x, const PARCPathName *y) +{ + if (x == y) { + return true; + } + if (x == NULL || y == NULL) { + return false; + } + + if (x->isAbsolute == y->isAbsolute) { + return parcDeque_Equals(x->path, y->path); + } + return false; +} + +bool +parcPathName_IsAbsolute(const PARCPathName *pathName) +{ + return pathName->isAbsolute; +} + +bool +parcPathName_MakeAbsolute(PARCPathName *pathName, bool absolute) +{ + bool result = parcPathName_IsAbsolute(pathName); + pathName->isAbsolute = absolute; + + return result; +} + +PARCPathName * +parcPathName_Prepend(PARCPathName *pathName, const char *name) +{ + parcDeque_Prepend(pathName->path, parcMemory_StringDuplicate(name, strlen(name))); + return pathName; +} + +PARCPathName * +parcPathName_Append(PARCPathName *pathName, const char *name) +{ + parcDeque_Append(pathName->path, parcMemory_StringDuplicate(name, strlen(name))); + return pathName; +} + +char * +parcPathName_GetAtIndex(const PARCPathName *pathName, size_t index) +{ + return (char *) parcDeque_GetAtIndex(pathName->path, index); +} + +PARCPathName * +parcPathName_Head(const PARCPathName *pathName, size_t size) +{ + PARCPathName *result = parcPathName_Create(); + size_t maximum = parcPathName_Size(pathName) < size ? parcPathName_Size(pathName) : size; + + for (size_t i = 0; i < maximum; i++) { + parcPathName_Append(result, parcPathName_GetAtIndex(pathName, i)); + } + + parcPathName_MakeAbsolute(result, parcPathName_IsAbsolute(pathName)); + + return result; +} + +PARCPathName * +parcPathName_Tail(const PARCPathName *pathName, size_t size) +{ + PARCPathName *result = parcPathName_Create(); + if (size > parcPathName_Size(pathName)) { + size = parcPathName_Size(pathName); + } + + for (size_t i = parcPathName_Size(pathName) - size; i < parcPathName_Size(pathName); i++) { + parcPathName_Prepend(result, parcPathName_GetAtIndex(pathName, i)); + } + + parcPathName_MakeAbsolute(result, false); + + return result; +} + +size_t +parcPathName_Size(const PARCPathName *pathName) +{ + return parcDeque_Size(pathName->path); +} + +PARCBufferComposer * +parcPathName_BuildString(const PARCPathName *pathName, PARCBufferComposer *composer) +{ + char *separator = "/"; + + // an absolute path with no segments should just be '/' + if (parcPathName_IsAbsolute(pathName)) { + parcBufferComposer_PutString(composer, separator); + } + + size_t length = parcDeque_Size(pathName->path); + if (length > 0) { + parcBufferComposer_PutString(composer, parcDeque_GetAtIndex(pathName->path, 0)); + for (size_t i = 1; i < length; i++) { + parcBufferComposer_PutStrings(composer, separator, parcDeque_GetAtIndex(pathName->path, i), NULL); + } + } + + return composer; +} + +char * +parcPathName_ToString(const PARCPathName *pathName) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + parcPathName_BuildString(pathName, composer); + + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + char *result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + + parcBufferComposer_Release(&composer); + + return result; +} + +void +parcPathName_Display(const PARCPathName *pathName, int indentation) +{ + if (pathName == NULL) { + parcDisplayIndented_PrintLine(indentation, "PARCPathName@NULL\n"); + } else { + parcDisplayIndented_PrintLine(indentation, "PARCPathName@%p {\n", (void *) pathName); + parcDeque_Display(pathName->path, indentation + 1); + parcDisplayIndented_PrintLine(indentation, "}\n"); + } +} diff --git a/libparc/parc/algol/parc_PathName.h b/libparc/parc/algol/parc_PathName.h new file mode 100644 index 00000000..d02f8ef5 --- /dev/null +++ b/libparc/parc/algol/parc_PathName.h @@ -0,0 +1,392 @@ +/* + * 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. + */ + +/** + * @file parc_PathName.h + * @ingroup inputoutput + * @brief Path Name Manipulation + * + */ +#ifndef libparc_parc_PathName_h +#define libparc_parc_PathName_h + +struct parc_pathname; +typedef struct parc_pathname PARCPathName; + +#include <parc/algol/parc_BufferComposer.h> + +/** + * Create an empty, relative, `PARCPathName`. + * + * @return A pointer to a `PARCPathName` instance. + * + * @see {@link parcPathName_MakeAbsolute} + * + * Example: + * @code + * { + * PARCPathName *result = parcPathName_Create(); + * parcPathName_Destroy(&result); + * } + * <#example#> + * @endcode + */ +PARCPathName *parcPathName_Create(void); + +/** + * Parse a null-terminated C string as a `PARCPathName` limited to specific length. + * Components are separated by a single '/' character. + * + * @param [in] limit The limit to the length + * @param [in] path The string to parse + * + * @return A pointer to the new `PARCPathName` + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCPathName *parcPathName_ParseToLimit(size_t limit, const char *path); + +/** + * Parse a null-terminated C string as a `PARCPathName` + * Components are separated by a single '/' character. + * + * @param [in] path The string to be parsed + * @return A pointer to the new `PARCPathName` + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCPathName *parcPathName_Parse(const char *path); + +/** + * Acquire a new reference to an instance of `PARCPathName`. + * + * The reference count to the instance is incremented. + * + * @param [in] pathName The instance of `PARCPathName` to which to refer. + * + * @return The same value as the input parameter @p pathName + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +PARCPathName *parcPathName_Acquire(const PARCPathName *pathName); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] pathNamePtr A pointer to a pointer to the instance to release. + * + * + * Example: + * @code + * { + * PARCPathName *pathName = parcPathName_Parse("/tmp/foo"); + * + * parcPathName_Release(&pathName); + * } + * @endcode + */ +void parcPathName_Release(PARCPathName **pathNamePtr); + +/** + * Determine if two `PARCPathName` instances are equal. + * + * The following equivalence relations on non-null `PARCPathName` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, `parcPathName_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `parcPathName_Equals(x, y)` must return true if and only if + * `parcPathName_Equals(y x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcPathName_Equals(x, y)` returns true and + * `parcPathName_Equals(y, z)` returns true, + * then `parcPathName_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `parcPathName_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `parcPathName_Equals(x, NULL)` must return false. + * + * @param [in] x A pointer to a `PARCPathName` instance. + * @param [in] y A pointer to a `PARCPathName` instance. + * + * @return true `PARCPathName` x and y are equal. + * @return false `PARCPathName` x and y are not equal. + * + * Example: + * @code + * { + * PARCPathName *a = parcPathName_Create(); + * PARCPathName *b = parcPathName_Create(); + * + * if (parcPathName_Equals(a, b)) { + * // true + * } else { + * // false + * } + * } + * @endcode + * + */ +bool parcPathName_Equals(const PARCPathName *x, const PARCPathName *y); + +/** + * Copy a pathName into a new instance of `PARCPathName` + * + * @param [in] pathName An instance of `PARCPathName` to be copied + * + * @return A new instance of `PARCPathName` that is a copy of @p pathName + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + * + */ +PARCPathName *parcPathName_Copy(const PARCPathName *pathName); + +/** + * Return true if the instance of `PARCPathName` is absolute + * + * An absolute path name is a fully specified path starting at the root as the left-most segment. + * A relative path name is an incomplete path starting at an unspecified root. + * + * For example, an absolute UNIX file name path begins with a `/` character. + * `/foo/bar`, `/tmp/test` are both absolute path names. + * + * @param [in] pathName The instance of `PARCPathName` to test for absoluteness + * @return True is the path name is absolute, false otherwise. + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool parcPathName_IsAbsolute(const PARCPathName *pathName); + +/** + * Make a `PARCPathName` absolute or relative. + * + * An absolute path name is a fully specified path starting at the root as the left-most segment. + * A relative path name is an incomplete path starting at an unspecified root. + * + * For example, an absolute UNIX file name path begins with a `/` character. + * `/foo/bar`, `/tmp/test` are both absolute path names. + * + * + * @param [in,out] pathName A pointer to a `PARCPathName` instance to be modified + * @param [in] absolute a flad as to whether the @p pathName should be set to absolute or relative; true indicates absolute + * + * @return true if the `PARCPathName` was previously an absolute path name. + * @return false if the `PARCPathName` was previously a relative path name. + * + * Example: + * @code + * { + * parcPathName_MakeAbsolute(pathName true) + * } + * @endcode + */ +bool parcPathName_MakeAbsolute(PARCPathName *pathName, bool absolute); + +/** + * Append a name segment to a `PARCPathName` + * + * The C string, `name` is copied. + * + * @param [in,out] pathName The instance of `PARCPathName` to modify + * @param [in] name A pointer to a null-terminated string. The contents are appended to the @p pathName. + * + * @return The input @p pathName + * + * Example: + * @code + * <#example#> + * @endcode + * + */ +PARCPathName *parcPathName_Append(PARCPathName *pathName, const char *name); + +/** + * Prepend a name segment to a `PARCPathName` + * + * The C string, `name` is copied. + * + * @param [in,out] pathName The instance of `PARCPathName` to modify + * @param [in] name A pointer to a null-terminated string. The contents are prepended to the @p pathName. + * + * @return The input @p pathName + * + * Example: + * @code + * <#example#> + * @endcode + * + */ +PARCPathName *parcPathName_Prepend(PARCPathName *pathName, const char *name); + +/** + * Create a new `PARCPathName` instance that consists of the head of an existing `PARCPathName`. + * + * If the original `PARCPathName` is an absolute path, the new `PARCPathName` will also be absolute. + * Otherwise, it will be a relative path. + * + * The new `PARCPathName` contains a copy of the requisite components of the orignal `PARCPathName`. + * + * @param [in] pathName The original `PARCPathName` + * @param [in] size The maximum number of path segments to include in the new `PARCPathName`. + * + * @return a Pointer to the new `PARCPathName` + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCPathName *parcPathName_Head(const PARCPathName *pathName, size_t size); + +/** + * Create a new `PARCPathName` instance that consists of the tail of an existing `PARCPathName`. + * + * The new `PARCPathName` is a relative path and contains a copy of the requisite components of the orignal `PARCPathName`. + * + * @param [in] pathName The original `PARCPathName` + * @param [in] size The maximum number of path segments to include in the new `PARCPathName`. + * + * @return a Pointer to the new `PARCPathName` + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCPathName *parcPathName_Tail(const PARCPathName *pathName, size_t size); + +/** + * Get a pointer to the null-terminated C string for the specified path name segment. + * + * @param [in] pathName A pointer to a `PARCPathName` instance. + * @param [in] index The index of the segment + * + * @return a pointer to the null-terminate C string for the specified path name segment + * + * Example: + * @code + * <#example#> + * @endcode + * + */ +char *parcPathName_GetAtIndex(const PARCPathName *pathName, size_t index); + +/** + * Get the number of path segments in a `PARCPathName`. + * + * @param [in] pathName A pointer to a `PARCPathName` instance. + * + * @return The number of path segments in the `PARCPathName` + * + * Example: + * @code + * <#example#> + * @endcode + * + */ +size_t parcPathName_Size(const PARCPathName *pathName); + +/** + * Append a representation of the specified instance to the given {@link PARCBufferComposer}. + * + * @param [in] path A pointer to the `PARCPathName` whose representation should be added to the @p string. + * @param [in] string A pointer to the `PARCBufferComposer` to which to append the representation of @p path + * + * + * @return NULL Cannot allocate memory. + * @return non-NULL The given `PARCBufferComposer`. + * + * Example: + * @code + * { + * PARCBufferComposer *result = parcBufferComposer_Create(); + * + * parcPathName_BuildString(instance, result); + * + * PARCBuffer *string = parcBufferComposer_FinalizeBuffer(result); + * printf("Hello: %s\n", parcBuffer_ToString(string)); + * parcBuffer_Release(&string); + * + * parcBufferComposer_Release(&result); + * } + * @endcode + */ +PARCBufferComposer *parcPathName_BuildString(const PARCPathName *path, PARCBufferComposer *string); + +/** + * Produce a C string representation of the given `PARCPathName`. + * + * Produce an allocated, null-terminated string representation of the given `PARCPathName`. + * The result must be freed by the caller via the `parcMemory_Deallocate()` function. + * + * @param [in] pathName A pointer to the `PARCPathName` to convert to a `String` + * + * @return The string representation of @p pathName + * + * Example: + * @code + * <#example#> + * @endcode + * + */ +char *parcPathName_ToString(const PARCPathName *pathName); + +/** + * Print a human readable representation of the given `PARCPathName`. + * + * @param [in] pathName A pointer to the instance of `PARCPathName` to display. + * @param [in] indentation The level of indentation to use to pretty-print the output. + * + * Example: + * @code + * { + * PARCPathName *instance = parcPathName_Create(); + * + * parcPathName_Display(instance, 0); + * + * parcPathName_Release(&instance); + * } + * @endcode + * + */ +void parcPathName_Display(const PARCPathName *pathName, int indentation); +#endif // libparc_parc_PathName_h diff --git a/libparc/parc/algol/parc_PriorityQueue.c b/libparc/parc/algol/parc_PriorityQueue.c new file mode 100755 index 00000000..93744b2c --- /dev/null +++ b/libparc/parc/algol/parc_PriorityQueue.c @@ -0,0 +1,387 @@ +/* + * 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. + */ + + +/** + * Priority Queue implemented over a Binary Heap. + * + * A Binary Heap will have average insert of O(1) and delete of O(log n). The worst case + * is O(log n) for both. The average and worst case FindMin is O(1). + * + * The binary heap is implemented as a "0"-based array, so for node index n, the + * children are at 2n+1 and 2n+2. Its parent is at floor((n-1)/2). + * + * The Heap property is a[n] <= a[2n+1] and a[k] <= a[2k+2]. We need to move things around + * sufficiently for this property to remain true. + * + */ + +#include <config.h> + +#include <LongBow/runtime.h> + +#include <stdio.h> +#include <stdbool.h> +#include <string.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_PriorityQueue.h> + +typedef struct heap_entry { + void *data; +} HeapEntry; + +struct parc_priority_queue { + HeapEntry *array; + size_t capacity; // how many elements are allocated + size_t size; // how many elements are used + + PARCPriorityQueueCompareTo *compare; + PARCPriorityQueueDestroyer *destroyer; +}; + +/** + * 0-based array indexing, so use 2n+1 + */ +static size_t +_leftChildIndex(size_t elementIndex) +{ + return 2 * elementIndex + 1; +} + +/** + * 0-based array indexing, so use 2n+2 + * IMPORTANT: This must be a larger index than the left child, + * as the TrickleDown algorithm assumes that if the right child exists so + * does the left child. + */ +static size_t +_rightChildIndex(size_t elementIndex) +{ + return 2 * elementIndex + 2; +} + +/** + * 0-based array indexing, so use (n-1)/2 + */ +static size_t +_parentIndex(size_t elementIndex) +{ + return (elementIndex - 1) / 2; +} + +/** + * Exchange the data between two array locations + */ +static void +_swap(PARCPriorityQueue *queue, size_t firstIndex, size_t secondIndex) +{ + void *firstData = queue->array[firstIndex].data; + queue->array[firstIndex].data = queue->array[secondIndex].data; + queue->array[secondIndex].data = firstData; +} + +/** + * See parcPriorityQueue_TrickleDown for full details + * + * Case 1: Right child exists and r.value < n.value && r.value < l.value + * In this case, swap(n.index, r.index) and set n.index = r.index. + * 50 6 + * / \ ===> / \ + * 9 6 9 50 + * + * Case 2: Right child exists and r.value < n.value && l.value <= r.value + * In this case swap(n.index, l.index) and set n.index = l.index + * This makes sense by transitivity that l <= r < n, so swap(n,l) satisfies the invariant. + * 50 6 + * / \ ===> / \ + * 6 9 50 9 + */ +static size_t +_trickleRightChild(PARCPriorityQueue *queue, size_t elementIndex, size_t leftChildIndex, size_t rightChildIndex) +{ + if (queue->compare(queue->array[rightChildIndex].data, queue->array[elementIndex].data) < 0) { + if (queue->compare(queue->array[rightChildIndex].data, queue->array[leftChildIndex].data) < 0) { + // Case 1 + _swap(queue, rightChildIndex, elementIndex); + elementIndex = rightChildIndex; + } else { + // Case 2 + _swap(queue, leftChildIndex, elementIndex); + elementIndex = leftChildIndex; + } + } + return elementIndex; +} + +/** + * See parcPriorityQueue_TrickleDown for full details + * + * Case 3: Left child exists (right does not) and l.value < n.value + * In this case, swap(n.index, l.index) and set n.index = l.index + * 50 6 + * / \ ===> / \ + * 6 x 50 x + */ +static size_t +_trickleLeftChild(PARCPriorityQueue *queue, size_t elementIndex, size_t leftChildIndex) +{ + if (queue->compare(queue->array[leftChildIndex].data, queue->array[elementIndex].data) < 0) { + // Case 3 + _swap(queue, leftChildIndex, elementIndex); + elementIndex = leftChildIndex; + } + return elementIndex; +} + +/** + * Moves an element down the heap until it satisfies the heap invariant. + * + * The value of node n must be less than or equal to both the left child and right child, if + * they exist. Here's the algorithm by example. Let r.value, l.value and n.value be the values + * of the right, left and node. Let r.index, l.index, and n.index be their indicies. + * + * Case 1: Right child exists and r.value < n.value && r.value < l.value + * In this case, swap(n.index, r.index) and set n.index = r.index. + * 50 6 + * / \ ===> / \ + * 9 6 9 50 + * + * Case 2: Right child exists and r.value < n.value && l.value <= r.value + * In this case swap(n.index, l.index) and set n.index = l.index + * This makes sense by transitivity that l <= r < n, so swap(n,l) satisfies the invariant. + * 50 6 + * / \ ===> / \ + * 6 9 50 9 + * + * Case 3: Left child exists (right does not) and l.value < n.value + * In this case, swap(n.index, l.index) and set n.index = l.index + * 50 6 + * / \ ===> / \ + * 6 x 50 x + * + * Case 4: No child exists or already satisfies invariant + * Done + * 50 50 + * / \ ===> / \ + * x x x x + * OR + * 4 4 + * / \ ===> / \ + * 9 6 9 6 + * + * + * @param [in] queue The priority queue to manipulate + * @param [in] elementIndex The root element (n above) to trickle down + * + * Example: + * @code + * <#example#> + * @endcode + */ +static void +_trickleDown(PARCPriorityQueue *queue, size_t elementIndex) +{ + bool finished = false; + + while (!finished) { + size_t rightChildIndex = _rightChildIndex(elementIndex); + size_t leftChildIndex = _leftChildIndex(elementIndex); + + if (rightChildIndex < queue->size) { + // Case 1 and Case 2 + elementIndex = _trickleRightChild(queue, elementIndex, leftChildIndex, rightChildIndex); + } else if (leftChildIndex < queue->size) { + // Case 3 + elementIndex = _trickleLeftChild(queue, elementIndex, leftChildIndex); + } else { + // Case 4, we're done + finished = true; + } + } +} + +/** + * Move the item at elementIndex up the tree until it satisfies the invariant. + * + * This is used when we insert an element at the bottom of the heap. We bubble it up + * the heap until it satisfies the heap invariant (i.e. it's parent is less than or + * equal to it). + * + * @param [in] queue The priority queue to manipulate + * @param [in] elementIndex The 0-based index of the element to bubble up + */ +static void +_bubbleUp(PARCPriorityQueue *queue, size_t elementIndex) +{ + size_t parentIndex = _parentIndex(elementIndex); + while (elementIndex > 0 && queue->compare(queue->array[elementIndex].data, queue->array[parentIndex].data) < 0) { + _swap(queue, elementIndex, parentIndex); + // now move up the ladder + elementIndex = parentIndex; + parentIndex = _parentIndex(elementIndex); + } + + // At this point, it is either at the top (elementIndex = 0) or statisfies the heap invariant. +} + +/** + * Add more elements to the backing aray + * + * Expand the array capacity. We use a fixed x2 expansion each time, though this might not + * be desirable when the capacity gets large. + * + * @param [in] queue The priority queue to manipulate + * + * Example: + * @code + * <#example#> + * @endcode + */ +static void +_expand(PARCPriorityQueue *queue) +{ + queue->capacity *= 2; + queue->array = parcMemory_Reallocate(queue->array, sizeof(HeapEntry) * queue->capacity); +} + +// ================================ +// Public API + +void +parcPriorityQueue_ParcFreeDestroyer(void **elementPtr) +{ + assertNotNull(elementPtr, "Double pointer must be non-null"); + assertNotNull(*elementPtr, "Double pointer must dereference to non-null"); + void *element = *elementPtr; + parcMemory_Deallocate((void **) &element); + *elementPtr = NULL; +} + +int +parcPriorityQueue_Uint64CompareTo(const void *a, const void *b) +{ + uint64_t value_a = *((uint64_t *) a); + uint64_t value_b = *((uint64_t *) b); + if (value_a < value_b) { + return -1; + } else if (value_a > value_b) { + return +1; + } + return 0; +} + +PARCPriorityQueue * +parcPriorityQueue_Create(PARCPriorityQueueCompareTo *compare, PARCPriorityQueueDestroyer *destroyer) +{ + assertNotNull(compare, "Parameter compare must be non-null"); + + size_t initialSize = 128; + PARCPriorityQueue *queue = parcMemory_AllocateAndClear(sizeof(PARCPriorityQueue)); + assertNotNull(queue, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(PARCPriorityQueue)); + queue->array = parcMemory_AllocateAndClear(sizeof(HeapEntry) * initialSize); + assertNotNull(queue->array, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(HeapEntry) * initialSize); + queue->capacity = initialSize; + queue->size = 0; + queue->compare = compare; + queue->destroyer = destroyer; + + return queue; +} + +void +parcPriorityQueue_Destroy(PARCPriorityQueue **queuePtr) +{ + assertNotNull(queuePtr, "Double pointer must be non-null"); + assertNotNull(*queuePtr, "Double pointer must dereference to non-null"); + PARCPriorityQueue *queue = *queuePtr; + parcPriorityQueue_Clear(queue); + parcMemory_Deallocate((void **) &(queue->array)); + parcMemory_Deallocate((void **) &queue); + *queuePtr = NULL; +} + +bool +parcPriorityQueue_Add(PARCPriorityQueue *queue, void *data) +{ + assertNotNull(queue, "Parameter queue must be non-null"); + assertNotNull(data, "Parameter data must be non-null"); + + if (queue->size + 1 > queue->capacity) { + _expand(queue); + } + + // insert at the end of the array + queue->array[queue->size].data = data; + + // increment the size before calling bubble up so invariants are true (i.e. + // the index we're giving to BubbleUp is within the array size. + queue->size++; + _bubbleUp(queue, queue->size - 1); + + // we always allow duplicates, so always return true + return true; +} + +void +parcPriorityQueue_Clear(PARCPriorityQueue *queue) +{ + assertNotNull(queue, "Parameter queue must be non-null"); + if (queue->destroyer != NULL) { + for (size_t i = 0; i < queue->size; i++) { + queue->destroyer(&queue->array[i].data); + } + } + + queue->size = 0; +} + +void * +parcPriorityQueue_Peek(PARCPriorityQueue *queue) +{ + assertNotNull(queue, "Parameter queue must be non-null"); + if (queue->size > 0) { + return queue->array[0].data; + } + return NULL; +} + +void * +parcPriorityQueue_Poll(PARCPriorityQueue *queue) +{ + assertNotNull(queue, "Parameter queue must be non-null"); + if (queue->size > 0) { + void *data = queue->array[0].data; + + queue->size--; + + // if size == 1, then this is a no-op + queue->array[0].data = queue->array[queue->size].data; + + // make sure the head satisifies the heap invariant + _trickleDown(queue, 0); + + return data; + } + + return NULL; +} + +size_t +parcPriorityQueue_Size(const PARCPriorityQueue *queue) +{ + assertNotNull(queue, "Parameter queue must be non-null"); + return queue->size; +} diff --git a/libparc/parc/algol/parc_PriorityQueue.h b/libparc/parc/algol/parc_PriorityQueue.h new file mode 100755 index 00000000..45a6ffab --- /dev/null +++ b/libparc/parc/algol/parc_PriorityQueue.h @@ -0,0 +1,223 @@ +/* + * 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. + */ + +/** + * @file parc_PriorityQueue.h + * @ingroup datastructures + * @brief A priority queue (heap), where the top item is the minimum by the sort function. + * + * The user provides a sort function and the top item will be the minimum + * as per the < relation. + * + */ + +#ifndef libparc_parc_PriorityQueue_h +#define libparc_parc_PriorityQueue_h + +#include <stdlib.h> +#include <stdbool.h> + +struct parc_priority_queue; +typedef struct parc_priority_queue PARCPriorityQueue; + +typedef int (PARCPriorityQueueCompareTo)(const void *a, const void *b); +typedef void (PARCPriorityQueueDestroyer)(void **elementPtr); + +/** + * Calls {@link parcMemory_Deallocate} to free the element + * + * A simple destroyer that only uses {@link parcMemory_Deallocate}. + * + * @param [in,out] elementPtr Double pointer to data item, will be NULL'd + * + * Example: + * @code + * + * PARCPriorityQueue *q = parcPriorityQueue_Create(parcPriorityQueue_Uint64CompareTo, parcPriorityQueue_ParcFreeDestroyer); + * uint64_t *objectid = parcMemory_Allocate(sizeof(uint64_t)); + * objectid = 100; + * + * // this will use parcPriorityQueue_Uint64CompareTo sort order + * parcPriorityQueue_Add(q, objectid); + * + * // this will use the ParcFreeDestroyer + * parcPriorityQueue_Destroy(&q); + * @endcode + */ +void parcPriorityQueue_ParcFreeDestroyer(void **elementPtr); + +/** + * Treats the parameters as `uint64_t` pointers and compares them via natural sort order. + * + * Treats the parameters as `uint64_t` pointers and compares them via natural sort order. + * Obeys standared CompareTo semantics. + * + * @param [in] a uint64_t pointer + * @param [in] b uint64_t pointer + * + * @return -1 if a < b + * @return 0 if a == b + * @return +1 if a > b + * + * Example: + * @code + * + * PARCPriorityQueue *q = parcPriorityQueue_Create(parcPriorityQueue_Uint64CompareTo, parcPriorityQueue_ParcFreeDestroyer); + * uint64_t *objectid = parcMemory_Allocate(sizeof(uint64_t)); + * objectid = 100; + * + * // this will use parcPriorityQueue_Uint64CompareTo sort order + * parcPriorityQueue_Add(q, objectid); + * + * // this will use the ParcFreeDestroyer + * parcPriorityQueue_Destroy(&q); + * @endcode + */ +int parcPriorityQueue_Uint64CompareTo(const void *a, const void *b); + + +/** + * Creates a priority queue with a given sort function. + * + * The sort function defines the ordering of the Priorty Queue. The minimum element + * will always be the head of the queue. + * + * The destroyer is called on data elements from {@link parcPriorityQueue_Clear()} and + * {@link parcPriorityQueue_Destroy()}. You may use {@linkparcPriorityQueue_ParcFreeDestroyer()} for + * elements that can be freed by only calling {@link parcMemory_Deallocate}. + * + * @param [in] compare Defines the sort order of the priority queue + * @param [in] destroyer Called for Clear and Destroy operations, may be NULL. + * + * @return non-null A pointer to a `PARCPriorityQueue` + * + * Example: + * @code + * PARCPriorityQueue *q = parcPriorityQueue_Create(parcPriorityQueue_Uint64CompareTo, parcPriorityQueue_ParcFreeDestroyer); + * uint64_t *objectid = parcMemory_Allocate(sizeof(uint64_t)); + * objectid = 100; + * + * // this will use parcPriorityQueue_Uint64CompareTo sort order + * parcPriorityQueue_Add(q, objectid); + * + * // this will use the ParcFreeDestroyer + * parcPriorityQueue_Destroy(&q); + * @endcode + */ +PARCPriorityQueue *parcPriorityQueue_Create(PARCPriorityQueueCompareTo *compare, PARCPriorityQueueDestroyer *destroyer); + + +/** + * Destroy the queue and free remaining elements. + * + * Destroys the queue. If the destroyer was set in Create, then it will be called + * on all the remaining elements. + * + * @param [in,out] queuePtr Double pointer to allocated queue. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void parcPriorityQueue_Destroy(PARCPriorityQueue **queuePtr); + +/** + * Add an element to the priority queue, returning true if changed + * + * A "duplicate" is a data item that compares as equal to another item. The priority + * queue supports duplicates. It is not stable in regard to the ordering of duplicates. + * Because it supports duplicates, Add will always return true. + * + * The priority queue is unbounded. + * + * @param [in,out] queue The queue to modify + * @param [in] data The data to add to the queue, which must be comparable and not NULL + * + * @return true The data structure was modified by adding the new value + * @return false The data structure was not modified + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool parcPriorityQueue_Add(PARCPriorityQueue *queue, void *data); + +/** + * Removes all elements, calling the data structure's destroyer on each + * + * Remvoes all elements. If the data structure's destroyer is non-NULL, it will be called + * on each element. + * + * @param [in,out] queue The queue to modify + * + * Example: + * @code + * <#example#> + * @endcode + */ +void parcPriorityQueue_Clear(PARCPriorityQueue *queue); + +/** + * Returns the head element, but does not remove it. + * + * Returns the head element. The data structure is not modified. If the + * priority queue is empty, will return NULL. + * + * @param [in] queue The `PARCPriorityQueue` to query. + * + * @return non-null The head element + * @return null The queue is empty + * + * Example: + * @code + * <#example#> + * @endcode + */ +void *parcPriorityQueue_Peek(PARCPriorityQueue *queue); + +/** + * Removes the head element from the queue and returns it. + * + * Removes the head element from the queue and returns it. If the queue is empty, + * it returns NULL. + * + * @param [in,out] queue The queue to query and modify. + * + * @return non-null The head element + * @return null The queue is empty + * + * Example: + * @code + * <#example#> + * @endcode + */ +void *parcPriorityQueue_Poll(PARCPriorityQueue *queue); + +/** + * Returns the number of elements in the queue. + * + * @param [in] queue The `PARCPriorityQueue` to query. + * + * @return number The number of elements in the queue. + * + * Example: + * @code + * <#example#> + * @endcode + */ +size_t parcPriorityQueue_Size(const PARCPriorityQueue *queue); +#endif // libparc_parc_PriorityQueue_h diff --git a/libparc/parc/algol/parc_Properties.c b/libparc/parc/algol/parc_Properties.c new file mode 100644 index 00000000..19aea290 --- /dev/null +++ b/libparc/parc/algol/parc_Properties.c @@ -0,0 +1,342 @@ +/* + * 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 <stdio.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_DisplayIndented.h> +#include <parc/algol/parc_HashMap.h> +#include <parc/algol/parc_Buffer.h> +#include <parc/algol/parc_Memory.h> + +#include "parc_Properties.h" + +struct PARCProperties { + PARCHashMap *properties; +}; + +static void +_parcProperties_Finalize(PARCProperties **instancePtr) +{ + assertNotNull(instancePtr, "Parameter must be a non-null pointer to a PARCProperties pointer."); + PARCProperties *instance = *instancePtr; + + parcProperties_OptionalAssertValid(instance); + + parcHashMap_Release(&instance->properties); +} + +parcObject_ImplementAcquire(parcProperties, PARCProperties); + +parcObject_ImplementRelease(parcProperties, PARCProperties); + +parcObject_ExtendPARCObject(PARCProperties, _parcProperties_Finalize, parcProperties_Copy, parcProperties_ToString, parcProperties_Equals, parcProperties_Compare, parcProperties_HashCode, parcProperties_ToJSON); + + +void +parcProperties_AssertValid(const PARCProperties *instance) +{ + assertTrue(parcProperties_IsValid(instance), + "PARCProperties is not valid."); +} + +PARCProperties * +parcProperties_Create(void) +{ + PARCProperties *result = parcObject_CreateInstance(PARCProperties); + + if (result != NULL) { + result->properties = parcHashMap_Create(); + } + + return result; +} + +int +parcProperties_Compare(const PARCProperties *instance, const PARCProperties *other) +{ + int result = 0; + + + return result; +} + +PARCProperties * +parcProperties_Copy(const PARCProperties *original) +{ + PARCProperties *result = parcObject_CreateInstance(PARCProperties); + + if (result != NULL) { + result->properties = parcHashMap_Copy(original->properties); + } + + return result; +} + +void +parcProperties_Display(const PARCProperties *properties, int indentation) +{ + parcDisplayIndented_PrintLine(indentation, "PARCProperties@%p {", properties); + trapCannotObtainLockIf(parcHashMap_Lock(properties->properties) == false, "Cannot lock PARCProperties object."); + + PARCIterator *iterator = parcHashMap_CreateKeyIterator(properties->properties); + while (parcIterator_HasNext(iterator)) { + char *key = parcBuffer_ToString(parcIterator_Next(iterator)); + const char *value = parcProperties_GetProperty(properties, key); + parcDisplayIndented_PrintLine(indentation + 1, "%s=%s", key, value); + + parcMemory_Deallocate(&key); + } + + parcIterator_Release(&iterator); + + parcHashMap_Unlock(properties->properties); + + parcDisplayIndented_PrintLine(indentation, "}"); +} + +bool +parcProperties_Equals(const PARCProperties *x, const PARCProperties *y) +{ + bool result = false; + + if (x == y) { + result = true; + } else if (x == NULL || y == NULL) { + result = false; + } else { + return parcHashMap_Equals(x->properties, y->properties); + } + + return result; +} + +PARCHashCode +parcProperties_HashCode(const PARCProperties *instance) +{ + return parcHashMap_HashCode(instance->properties); +} + +bool +parcProperties_IsValid(const PARCProperties *instance) +{ + bool result = false; + + if (instance != NULL) { + result = true; + } + + return result; +} + +PARCJSON * +parcProperties_ToJSON(const PARCProperties *properties) +{ + PARCJSON *result = parcJSON_Create(); + + trapCannotObtainLockIf(parcHashMap_Lock(properties->properties) == false, "Cannot lock PARCProperties object."); + + PARCIterator *iterator = parcHashMap_CreateKeyIterator(properties->properties); + while (parcIterator_HasNext(iterator)) { + char *key = parcBuffer_ToString(parcIterator_Next(iterator)); + const char *value = parcProperties_GetProperty(properties, key); + parcJSON_AddString(result, key, value); + parcMemory_Deallocate(&key); + } + + parcIterator_Release(&iterator); + + parcHashMap_Unlock(properties->properties); + return result; +} + +PARCBufferComposer * +parcProperties_BuildString(const PARCProperties *properties, PARCBufferComposer *composer) +{ + trapCannotObtainLockIf(parcHashMap_Lock(properties->properties) == false, "Cannot lock PARCProperties object."); + + PARCIterator *iterator = parcHashMap_CreateKeyIterator(properties->properties); + while (parcIterator_HasNext(iterator)) { + char *key = parcBuffer_ToString(parcIterator_Next(iterator)); + const char *value = parcProperties_GetProperty(properties, key); + parcBufferComposer_PutStrings(composer, key, "=", value, "\n", NULL); + parcMemory_Deallocate(&key); + } + + parcIterator_Release(&iterator); + + parcHashMap_Unlock(properties->properties); + return composer; +} + +char * +parcProperties_ToString(const PARCProperties *properties) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + parcProperties_BuildString(properties, composer); + char *result = parcBufferComposer_ToString(composer); + + parcBufferComposer_Release(&composer); + + return result; +} + +void +parcProperties_SetParsedProperty(PARCProperties *properties, char *string) +{ + char *equals = strchr(string, '='); + *equals++ = 0; + + parcProperties_SetProperty(properties, string, equals); +} + +bool +parcProperties_SetProperty(PARCProperties *properties, const char *name, const char *string) +{ + bool result = false; + + PARCBuffer *key = parcBuffer_AllocateCString(name); + PARCBuffer *value = parcBuffer_AllocateCString(string); + + parcHashMap_Put(properties->properties, key, value); + parcBuffer_Release(&key); + parcBuffer_Release(&value); + return result; +} + +const char * +parcProperties_GetProperty(const PARCProperties *properties, const char *name) +{ + char *result = NULL; + + PARCBuffer *key = parcBuffer_AllocateCString(name); + PARCBuffer *value = (PARCBuffer *) parcHashMap_Get(properties->properties, key); + if (value != NULL) { + result = parcBuffer_Overlay(value, 0); + } + + parcBuffer_Release(&key); + return result; +} + +const char * +parcProperties_GetPropertyDefault(const PARCProperties *properties, const char *restrict name, const char *restrict defaultValue) +{ + char *result = (char *) defaultValue; + + PARCBuffer *key = parcBuffer_AllocateCString(name); + PARCBuffer *value = (PARCBuffer *) parcHashMap_Get(properties->properties, key); + if (value != NULL) { + result = parcBuffer_Overlay(value, 0); + } + + parcBuffer_Release(&key); + return result; +} + +bool +parcProperties_GetAsBoolean(const PARCProperties *properties, const char *name, bool defaultValue) +{ + bool result = defaultValue; + + const char *value = parcProperties_GetProperty(properties, name); + if (value != NULL) { + if (strcmp(value, "true") == 0) { + result = true; + } else { + result = false; + } + } + + return result; +} + +int64_t +parcProperties_GetAsInteger(const PARCProperties *properties, const char *name, int64_t defaultValue) +{ + int64_t result = defaultValue; + + const char *value = parcProperties_GetProperty(properties, name); + if (value != NULL) { + result = strtol(value, NULL, 10); + } + + return result; +} + +typedef struct { + PARCBuffer *element; + PARCIterator *hashMapIterator; +} _PARCPropertiesIterator; + +static _PARCPropertiesIterator * +_parcPropertiesIterator_Init(const PARCProperties *object) +{ + _PARCPropertiesIterator *state = parcMemory_AllocateAndClear(sizeof(_PARCPropertiesIterator)); + state->hashMapIterator = parcHashMap_CreateKeyIterator(object->properties); + return state; +} + +static bool +_parcPropertiesIterator_HasNext(PARCProperties *properties __attribute__((unused)), _PARCPropertiesIterator *state) +{ + return parcIterator_HasNext(state->hashMapIterator); +} + +static _PARCPropertiesIterator * +_parcPropertiesIterator_Next(PARCProperties *properties __attribute__((unused)), _PARCPropertiesIterator *state) +{ + state->element = (PARCBuffer *) parcIterator_Next(state->hashMapIterator); + return state; +} + +static void +_parcPropertiesIterator_Remove(PARCProperties *properties __attribute__((unused)), _PARCPropertiesIterator **state) +{ + parcIterator_Remove((*state)->hashMapIterator); +} + +static char * +_parcPropertiesIterator_Element(PARCProperties *properties __attribute__((unused)), _PARCPropertiesIterator *state) +{ + return parcBuffer_Overlay(state->element, 0); +} + +static void +_parcPropertiesIterator_Fini(PARCProperties *properties __attribute__((unused)), _PARCPropertiesIterator *state) +{ + parcIterator_Release(&state->hashMapIterator); + parcMemory_Deallocate(&state); +} + +PARCIterator * +parcProperties_CreateIterator(const PARCProperties *properties) +{ + PARCIterator *iterator = parcIterator_Create((PARCObject *) properties, + (void *(*)(PARCObject *))_parcPropertiesIterator_Init, + (bool (*)(PARCObject *, void *))_parcPropertiesIterator_HasNext, + (void *(*)(PARCObject *, void *))_parcPropertiesIterator_Next, + (void (*)(PARCObject *, void **))_parcPropertiesIterator_Remove, + (void *(*)(PARCObject *, void *))_parcPropertiesIterator_Element, + (void (*)(PARCObject *, void *))_parcPropertiesIterator_Fini, + NULL); + + return iterator; +} diff --git a/libparc/parc/algol/parc_Properties.h b/libparc/parc/algol/parc_Properties.h new file mode 100644 index 00000000..6cca4d87 --- /dev/null +++ b/libparc/parc/algol/parc_Properties.h @@ -0,0 +1,502 @@ +/* + * 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. + */ + +/** + * @file parc_Properties.h + * @ingroup types + * @brief <#Brief Description#> + * + * <#Detailed Description#> + * + */ +#ifndef PARCLibrary_parc_Properties +#define PARCLibrary_parc_Properties +#include <stdbool.h> + +#include <parc/algol/parc_JSON.h> +#include <parc/algol/parc_HashCode.h> +#include <parc/algol/parc_Iterator.h> + +struct PARCProperties; +typedef struct PARCProperties PARCProperties; + +/** + * Increase the number of references to a `PARCProperties` instance. + * + * Note that new `PARCProperties` is not created, + * only that the given `PARCProperties` reference count is incremented. + * Discard the reference by invoking `parcProperties_Release`. + * + * @param [in] instance A pointer to a valid PARCProperties instance. + * + * @return The same value as @p instance. + * + * Example: + * @code + * { + * PARCProperties *a = parcProperties_Create(); + * + * PARCProperties *b = parcProperties_Acquire(); + * + * parcProperties_Release(&a); + * parcProperties_Release(&b); + * } + * @endcode + */ +PARCProperties *parcProperties_Acquire(const PARCProperties *instance); + +#ifdef PARCLibrary_DISABLE_VALIDATION +# define parcProperties_OptionalAssertValid(_instance_) +#else +# define parcProperties_OptionalAssertValid(_instance_) parcProperties_AssertValid(_instance_) +#endif + +/** + * Assert that the given `PARCProperties` instance is valid. + * + * @param [in] instance A pointer to a valid PARCProperties instance. + * + * Example: + * @code + * { + * PARCProperties *a = parcProperties_Create(); + * + * parcProperties_AssertValid(a); + * + * printf("Instance is valid.\n"); + * + * parcProperties_Release(&b); + * } + * @endcode + */ +void parcProperties_AssertValid(const PARCProperties *instance); + +/** + * Create an instance of PARCProperties + * + * @return non-NULL A pointer to a valid PARCProperties instance. + * @return NULL An error occurred. + * + * Example: + * @code + * { + * PARCProperties *a = parcProperties_Create(); + * + * parcProperties_Release(&a); + * } + * @endcode + */ +PARCProperties *parcProperties_Create(void); + +/** + * Compares @p instance with @p other for order. + * + * Returns a negative integer, zero, or a positive integer as @p instance + * is less than, equal to, or greater than @p other. + * + * @param [in] instance A pointer to a valid PARCProperties instance. + * @param [in] other A pointer to a valid PARCProperties instance. + * + * @return <0 Instance is less than @p other. + * @return 0 Instance a and instance b compare the same. + * @return >0 Instance a is greater than instance b. + * + * Example: + * @code + * { + * PARCProperties *a = parcProperties_Create(); + * PARCProperties *b = parcProperties_Create(); + * + * if (parcProperties_Compare(a, b) == 0) { + * printf("Instances are equal.\n"); + * } + * + * parcProperties_Release(&a); + * parcProperties_Release(&b); + * } + * @endcode + * + * @see parcProperties_Equals + */ +int parcProperties_Compare(const PARCProperties *instance, const PARCProperties *other); + +/** + * Create an independent copy the given `PARCBuffer` + * + * A new buffer is created as a complete copy of the original. + * + * @param [in] original A pointer to a valid PARCProperties instance. + * + * @return NULL Memory could not be allocated. + * @return non-NULL A pointer to a new `PARCProperties` instance. + * + * Example: + * @code + * { + * PARCProperties *a = parcProperties_Create(); + * + * PARCProperties *copy = parcProperties_Copy(&b); + * + * parcProperties_Release(&b); + * parcProperties_Release(©); + * } + * @endcode + */ +PARCProperties *parcProperties_Copy(const PARCProperties *original); + +/** + * Print a human readable representation of the given `PARCProperties`. + * + * @param [in] instance A pointer to a valid PARCProperties instance. + * @param [in] indentation The indentation level to use for printing. + * + * Example: + * @code + * { + * PARCProperties *a = parcProperties_Create(); + * + * parcProperties_Display(a, 0); + * + * parcProperties_Release(&a); + * } + * @endcode + */ +void parcProperties_Display(const PARCProperties *instance, int indentation); + +/** + * Determine if two `PARCProperties` instances are equal. + * + * The following equivalence relations on non-null `PARCProperties` instances are maintained: * + * * It is reflexive: for any non-null reference value x, `parcProperties_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `parcProperties_Equals(x, y)` must return true if and only if + * `parcProperties_Equals(y x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcProperties_Equals(x, y)` returns true and + * `parcProperties_Equals(y, z)` returns true, + * then `parcProperties_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `parcProperties_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `parcProperties_Equals(x, NULL)` must return false. + * + * @param [in] x A pointer to a valid PARCProperties instance. + * @param [in] y A pointer to a valid PARCProperties instance. + * + * @return true The instances x and y are equal. + * + * Example: + * @code + * { + * PARCProperties *a = parcProperties_Create(); + * PARCProperties *b = parcProperties_Create(); + * + * if (parcProperties_Equals(a, b)) { + * printf("Instances are equal.\n"); + * } + * + * parcProperties_Release(&a); + * parcProperties_Release(&b); + * } + * @endcode + * @see parcProperties_HashCode + */ +bool parcProperties_Equals(const PARCProperties *x, const PARCProperties *y); + +/** + * Returns a hash code value for the given instance. + * + * The general contract of `HashCode` is: + * + * Whenever it is invoked on the same instance more than once during an execution of an application, + * the `HashCode` function must consistently return the same value, + * provided no information used in a corresponding comparisons on the instance is modified. + * + * This value need not remain consistent from one execution of an application to another execution of the same application. + * If two instances are equal according to the {@link parcProperties_Equals} method, + * then calling the {@link parcProperties_HashCode} method on each of the two instances must produce the same integer result. + * + * It is not required that if two instances are unequal according to the + * {@link parcProperties_Equals} function, + * then calling the `parcProperties_HashCode` + * method on each of the two objects must produce distinct integer results. + * + * @param [in] instance A pointer to a valid PARCProperties instance. + * + * @return The hashcode for the given instance. + * + * Example: + * @code + * { + * PARCProperties *a = parcProperties_Create(); + * + * uint32_t hashValue = parcProperties_HashCode(buffer); + * parcProperties_Release(&a); + * } + * @endcode + */ +PARCHashCode parcProperties_HashCode(const PARCProperties *instance); + +/** + * Determine if an instance of `PARCProperties` is valid. + * + * Valid means the internal state of the type is consistent with its required current or future behaviour. + * This may include the validation of internal instances of types. + * + * @param [in] instance A pointer to a valid PARCProperties instance. + * + * @return true The instance is valid. + * @return false The instance is not valid. + * + * Example: + * @code + * { + * PARCProperties *a = parcProperties_Create(); + * + * if (parcProperties_IsValid(a)) { + * printf("Instance is valid.\n"); + * } + * + * parcProperties_Release(&a); + * } + * @endcode + * + */ +bool parcProperties_IsValid(const PARCProperties *instance); + +/** + * Release a previously acquired reference to the given `PARCProperties` instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] instancePtr A pointer to a pointer to the instance to release. + * + * Example: + * @code + * { + * PARCProperties *a = parcProperties_Create(); + * + * parcProperties_Release(&a); + * } + * @endcode + */ +void parcProperties_Release(PARCProperties **instancePtr); + +/** + * Create a `PARCJSON` instance (representation) of the given object. + * + * @param [in] instance A pointer to a valid PARCProperties instance. + * + * @return NULL Memory could not be allocated to contain the `PARCJSON` instance. + * @return non-NULL An allocated C string that must be deallocated via parcMemory_Deallocate(). + * + * Example: + * @code + * { + * PARCProperties *a = parcProperties_Create(); + * + * PARCJSON *json = parcProperties_ToJSON(a); + * + * printf("JSON representation: %s\n", parcJSON_ToString(json)); + * parcJSON_Release(&json); + * + * parcProperties_Release(&a); + * } + * @endcode + */ +PARCJSON *parcProperties_ToJSON(const PARCProperties *instance); + +/** + * Produce a null-terminated string representation of the specified `PARCProperties`. + * + * The result must be freed by the caller via {@link parcMemory_Deallocate}. + * + * @param [in] instance A pointer to a valid PARCProperties instance. + * + * @return NULL Cannot allocate memory. + * @return non-NULL A pointer to an allocated, null-terminated C string that must be deallocated via {@link parcMemory_Deallocate}. + * + * Example: + * @code + * { + * PARCProperties *a = parcProperties_Create(); + * + * char *string = parcProperties_ToString(a); + * + * parcProperties_Release(&a); + * + * parcMemory_Deallocate(&string); + * } + * @endcode + * + * @see parcProperties_Display + */ +char *parcProperties_ToString(const PARCProperties *instance); + +/** + * Append a representation of the specified `PARCProperties` instance to the given `PARCBufferComposer`. + * + * @param [in] properties A pointer to a `PARCProperties` instance whose representation should be appended to the @p composer. + * @param [in,out] composer A pointer to a `PARCBufferComposer` instance to be modified. + * + * @return NULL Cannot allocate memory. + * @return non-NULL The @p composer. + * + * Example: + * @code + * { + * PARCBufferComposer *result = parcBufferComposer_Create(); + * + * parcProperties_BuildString(instance, result); + * + * char *string = parcBufferComposer_ToString(result); + * printf("Hello: %s\n", string); + * parcMemory_Deallocate(string); + * + * parcBufferComposer_Release(&result); + * } + * @endcode + */ +PARCBufferComposer *parcProperties_BuildString(const PARCProperties *properties, PARCBufferComposer *composer); + +/** + * Set the named property to @p value. + * + * + * @param [in] properties A pointer to a valid `PARCProperties` instance. + * @param [in] name A pointer to a nul-terminate, C string name for the property. + * @param [in] value A A pointer to a nul-terminate, C string name for the value of the property. + * + * @return true <#explanation#> + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +bool parcProperties_SetProperty(PARCProperties *properties, const char *name, const char *value); + + +void parcProperties_SetParsedProperty(PARCProperties *properties, char *string); + +/** + * Get the string value of the named property. + * + * @param [in] properties A pointer to a valid PARCProperties intance. + * @param [in] name A pointer to a nul-terminated, C string containing the name of the property. + * + * @return non-NULL A nul-terminated C string containing the value of the named property. + * @return NULL The property was not found. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +const char *parcProperties_GetProperty(const PARCProperties *properties, const char *restrict name); + +/** + * Return the string value of the named property, if present. + * Otherwise return the default value. + * + * @param [in] properties A pointer to a valid PARCProperties instance. + * @param [in] name A pointer to a nul-terminated, C string containing the name of the property. + * @param [in] defaultValue A pointer to a nul-terminated, C string containing the name of the property. + * + * @return non-NULL A nul-terminated C string containing the value of the named property. + * @return NULL The property was not found, and the default property was NULL. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +const char *parcProperties_GetPropertyDefault(const PARCProperties *properties, const char *restrict name, const char *restrict defaultValue); + +/** + * Return the boolean value of the named property, if present. + * Otherwise return the default value. + * + * @param [in] properties A pointer to a valid PARCProperties instance. + * @param [in] name A pointer to a nul-terminated, C string containing the name of the property. + * @param [in] defaultValue The default boolean value. + * + * @return true A the property is set to true. + * @return false The property is either not present, or is not set to true. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +bool parcProperties_GetAsBoolean(const PARCProperties *properties, const char *name, bool defaultValue); + +/** + * Return the integer value of the named property, if present. + * Otherwise return the default value. + * + * @param [in] properties A pointer to a valid PARCProperties instance. + * @param [in] name A pointer to a nul-terminated, C string containing the name of the property. + * @param [in] defaultValue The default integer value. + * + * @return The integer value of the named property. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +int64_t parcProperties_GetAsInteger(const PARCProperties *properties, const char *name, int64_t defaultValue); + +/** + * Create a new instance of PARCIterator that iterates through the given PARCProperties. + * The returned value must be released via {@link parcIterator_Release}. + * + * @param [in] list A pointer to a valid `PARCProperties`. + * + * @see parcIterator_Release + * + * Example: + * @code + * { + * PARCIterator *iterator = parcLinkedList_CreateIterator(hashMap); + * + * while (parcIterator_HasNext(iterator)) { + * PARCObject *object = parcIterator_Next(iterator); + * } + * + * parcIterator_Release(&iterator); + * } + * @endcode + */ +PARCIterator *parcProperties_CreateIterator(const PARCProperties *properties); +#endif diff --git a/libparc/parc/algol/parc_RandomAccessFile.c b/libparc/parc/algol/parc_RandomAccessFile.c new file mode 100755 index 00000000..65bff1d1 --- /dev/null +++ b/libparc/parc/algol/parc_RandomAccessFile.c @@ -0,0 +1,185 @@ +/* + * 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 <parc/algol/parc_Object.h> +#include <parc/algol/parc_DisplayIndented.h> +#include <parc/algol/parc_Memory.h> + +#include <parc/algol/parc_RandomAccessFile.h> + +#include <stdio.h> + +struct PARCRandomAccessFile { + char *fname; + FILE *fhandle; +}; + +static void +_parcRandomAccessFile_Finalize(PARCRandomAccessFile **instancePtr) +{ + assertNotNull(instancePtr, "Parameter must be a non-null pointer to a PARCRandomAccessFile pointer."); + PARCRandomAccessFile *instance = *instancePtr; + if (instance->fhandle != NULL) { + fclose(instance->fhandle); + } + if (instance->fname != NULL) { + parcMemory_Deallocate(&instance->fname); + } +} + +parcObject_ImplementAcquire(parcRandomAccessFile, PARCRandomAccessFile); + +parcObject_ImplementRelease(parcRandomAccessFile, PARCRandomAccessFile); + +parcObject_ExtendPARCObject(PARCRandomAccessFile, _parcRandomAccessFile_Finalize, NULL, + parcRandomAccessFile_ToString, parcRandomAccessFile_Equals, NULL, + parcRandomAccessFile_HashCode, parcRandomAccessFile_ToJSON); + + +void +parcRandomAccessFile_AssertValid(const PARCRandomAccessFile *instance) +{ + assertTrue(parcRandomAccessFile_IsValid(instance), + "PARCRandomAccessFile is not valid."); +} + +void +parcRandomAccessFile_Display(const PARCRandomAccessFile *instance, int indentation) +{ + parcDisplayIndented_PrintLine(indentation, "PARCRandomAccessFile@%p {", instance); + parcDisplayIndented_PrintLine(indentation + 1, "File: %s", instance->fname); + parcDisplayIndented_PrintLine(indentation, "}"); +} + +PARCHashCode +parcRandomAccessFile_HashCode(const PARCRandomAccessFile *instance) +{ + return parcHashCode_Hash((uint8_t *) instance->fname, strlen(instance->fname)); +} + +bool +parcRandomAccessFile_Equals(const PARCRandomAccessFile *x, const PARCRandomAccessFile *y) +{ + bool result = false; + + if (x == y) { + result = true; + } else if (x == NULL || y == NULL) { + result = false; + } else { + if (strcmp(x->fname, y->fname) == 0) { + result = true; + } + } + + return result; +} + +bool +parcRandomAccessFile_IsValid(const PARCRandomAccessFile *instance) +{ + bool result = false; + + if (instance != NULL) { + result = instance->fhandle != NULL; + } + + return result; +} + +PARCJSON * +parcRandomAccessFile_ToJSON(const PARCRandomAccessFile *instance) +{ + PARCJSON *result = parcJSON_Create(); + + if (result != NULL) { + parcJSON_AddString(result, "fname", instance->fname); + } + + return result; +} + +char * +parcRandomAccessFile_ToString(const PARCRandomAccessFile *instance) +{ + char *result = parcMemory_Format("PARCRandomAccessFile[%s]@%p\n", instance->fname, instance); + return result; +} + +PARCRandomAccessFile * +parcRandomAccessFile_Open(PARCFile *file) +{ + PARCRandomAccessFile *handle = parcObject_CreateAndClearInstance(PARCRandomAccessFile); + if (handle != NULL) { + char *fname = parcFile_ToString(file); + handle->fhandle = fopen(fname, "r+"); + handle->fname = parcMemory_StringDuplicate(fname, strlen(fname)); + parcMemory_Deallocate(&fname); + } + return handle; +} + +bool +parcRandomAccessFile_Close(PARCRandomAccessFile *fileHandle) +{ + assertNotNull(fileHandle->fhandle, "Can't fclose a null pointer. How did they get one anyway?"); + bool result = fclose(fileHandle->fhandle) == 0; + fileHandle->fhandle = NULL; + parcMemory_Deallocate(&fileHandle->fname); + fileHandle->fname = NULL; + return result; +} + +// read (count * size) bytes into the provided buffer, and return the number of bytes actually read +size_t +parcRandomAccessFile_Read(PARCRandomAccessFile *fileHandle, PARCBuffer *buffer) +{ + size_t length = parcBuffer_Remaining(buffer); + size_t numBytes = fread(parcBuffer_Overlay(buffer, length), 1, length, fileHandle->fhandle); + return numBytes; +} + +// write (count * size) bytes from `buffer` to the file, and return the number of bytes actually written +size_t +parcRandomAccessFile_Write(PARCRandomAccessFile *fileHandle, PARCBuffer *buffer) +{ + size_t length = parcBuffer_Remaining(buffer); + size_t numBytes = fwrite(parcBuffer_Overlay(buffer, length), 1, length, fileHandle->fhandle); + return numBytes; +} + +size_t +parcRandomAccessFile_Seek(PARCRandomAccessFile *fileHandle, long offset, PARCRandomAccessFilePosition position) +{ + size_t result = 0; + switch (position) { + case PARCRandomAccessFilePosition_Start: + result = fseek(fileHandle->fhandle, offset, SEEK_SET); // beginning of the file + break; + case PARCRandomAccessFilePosition_Current: + result = fseek(fileHandle->fhandle, offset, SEEK_CUR); // current offset + break; + case PARCRandomAccessFilePosition_End: + result = fseek(fileHandle->fhandle, offset, SEEK_END); // end of the file + break; + default: + assertTrue(false, "Invalid position %d", position); + } + return result; +} diff --git a/libparc/parc/algol/parc_RandomAccessFile.h b/libparc/parc/algol/parc_RandomAccessFile.h new file mode 100755 index 00000000..f4f20150 --- /dev/null +++ b/libparc/parc/algol/parc_RandomAccessFile.h @@ -0,0 +1,426 @@ +/* + * 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. + */ + +/** + * @file parc_RandomAccessFile.h + * @ingroup inputoutput + * @brief A wrapper that provides random access to a file. + * + */ +#ifndef PARCLibrary_RandomAccessFile +#define PARCLibrary_RandomAccessFile +#include <stdbool.h> + +#include <parc/algol/parc_JSON.h> +#include <parc/algol/parc_HashCode.h> +#include <parc/algol/parc_File.h> + +struct PARCRandomAccessFile; +typedef struct PARCRandomAccessFile PARCRandomAccessFile; + +typedef enum { + PARCRandomAccessFilePosition_Start, + PARCRandomAccessFilePosition_End, + PARCRandomAccessFilePosition_Current +} PARCRandomAccessFilePosition; + +/** + * Increase the number of references to a `PARCRandomAccessFile` instance. + * + * Note that new `PARCRandomAccessFile` is not created, + * only that the given `PARCRandomAccessFile` reference count is incremented. + * Discard the reference by invoking `parcRandomAccessFile_Release`. + * + * @param [in] instance A pointer to a valid PARCRandomAccessFile instance. + * + * @return The same value as @p instance. + * + * Example: + * @code + * { + * PARCRandomAccessFile *a = parcRandomAccessFile_Open(..); + * + * PARCRandomAccessFile *b = parcRandomAccessFile_Acquire(); + * + * parcRandomAccessFile_Release(&a); + * parcRandomAccessFile_Release(&b); + * } + * @endcode + */ +PARCRandomAccessFile *parcRandomAccessFile_Acquire(const PARCRandomAccessFile *instance); + +#ifdef PARCLibrary_DISABLE_VALIDATION +# define parcRandomAccessFile_OptionalAssertValid(_instance_) +#else +# define parcRandomAccessFile_OptionalAssertValid(_instance_) parcRandomAccessFile_AssertValid(_instance_) +#endif + +/** + * Assert that the given `PARCRandomAccessFile` instance is valid. + * + * @param [in] instance A pointer to a valid PARCRandomAccessFile instance. + * + * Example: + * @code + * { + * PARCRandomAccessFile *a = parcRandomAccessFile_Open(..); + * + * parcRandomAccessFile_AssertValid(a); + * + * printf("Instance is valid.\n"); + * + * parcRandomAccessFile_Release(&b); + * } + * @endcode + */ +void parcRandomAccessFile_AssertValid(const PARCRandomAccessFile *instance); + +/** + * Print a human readable representation of the given `PARCRandomAccessFile`. + * + * @param [in] instance A pointer to a valid PARCRandomAccessFile instance. + * @param [in] indentation The indentation level to use for printing. + * + * Example: + * @code + * { + * PARCRandomAccessFile *a = parcRandomAccessFile_Open(..); + * + * parcRandomAccessFile_Display(a, 0); + * + * parcRandomAccessFile_Release(&a); + * } + * @endcode + */ +void parcRandomAccessFile_Display(const PARCRandomAccessFile *instance, int indentation); + +/** + * Determine if two `PARCRandomAccessFile` instances are equal. + * + * The following equivalence relations on non-null `PARCRandomAccessFile` instances are maintained: * + * * It is reflexive: for any non-null reference value x, `parcRandomAccessFile_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `parcRandomAccessFile_Equals(x, y)` must return true if and only if + * `parcRandomAccessFile_Equals(y x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcRandomAccessFile_Equals(x, y)` returns true and + * `parcRandomAccessFile_Equals(y, z)` returns true, + * then `parcRandomAccessFile_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `parcRandomAccessFile_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `parcRandomAccessFile_Equals(x, NULL)` must return false. + * + * @param [in] x A pointer to a valid PARCRandomAccessFile instance. + * @param [in] y A pointer to a valid PARCRandomAccessFile instance. + * + * @return true The instances x and y are equal. + * + * Example: + * @code + * { + * PARCRandomAccessFile *a = parcRandomAccessFile_Open(..); + * PARCRandomAccessFile *b = parcRandomAccessFile_Open(..); + * + * if (parcRandomAccessFile_Equals(a, b)) { + * printf("Instances are equal.\n"); + * } + * + * parcRandomAccessFile_Release(&a); + * parcRandomAccessFile_Release(&b); + * } + * @endcode + * @see parcRandomAccessFile_HashCode + */ +bool parcRandomAccessFile_Equals(const PARCRandomAccessFile *x, const PARCRandomAccessFile *y); + +/** + * Returns a hash code value for the given instance. + * + * The general contract of `HashCode` is: + * + * Whenever it is invoked on the same instance more than once during an execution of an application, + * the `HashCode` function must consistently return the same value, + * provided no information used in a corresponding comparisons on the instance is modified. + * + * This value need not remain consistent from one execution of an application to another execution of the same application. + * If two instances are equal according to the {@link parcRandomAccessFile_Equals} method, + * then calling the {@link parcRandomAccessFile_HashCode} method on each of the two instances must produce the same integer result. + * + * It is not required that if two instances are unequal according to the + * {@link parcRandomAccessFile_Equals} function, + * then calling the `parcRandomAccessFile_HashCode` + * method on each of the two objects must produce distinct integer results. + * + * @param [in] instance A pointer to a valid PARCRandomAccessFile instance. + * + * @return The hashcode for the given instance. + * + * Example: + * @code + * { + * PARCRandomAccessFile *a = parcRandomAccessFile_Open..(); + * + * PARCHashCode hashValue = parcRandomAccessFile_HashCode(buffer); + * parcRandomAccessFile_Release(&a); + * } + * @endcode + */ +PARCHashCode parcRandomAccessFile_HashCode(const PARCRandomAccessFile *instance); + +/** + * Determine if an instance of `PARCRandomAccessFile` is valid. + * + * Valid means the internal state of the type is consistent with its required current or future behaviour. + * This may include the validation of internal instances of types. + * + * @param [in] instance A pointer to a valid PARCRandomAccessFile instance. + * + * @return true The instance is valid. + * @return false The instance is not valid. + * + * Example: + * @code + * { + * PARCRandomAccessFile *a = parcRandomAccessFile_Open(..); + * + * if (parcRandomAccessFile_IsValid(a)) { + * printf("Instance is valid.\n"); + * } + * + * parcRandomAccessFile_Release(&a); + * } + * @endcode + * + */ +bool parcRandomAccessFile_IsValid(const PARCRandomAccessFile *instance); + +/** + * Open a new `PARCRandomAccessFile` instance. + * + * @param [in] file A `PARCFile` which refers to the file to open for random access. + * + * @retval `PARCRandomAccessFile` A new instance + * @retval NULL An error occurred. + * + * Example: + * @code + * { + * PARCFile *file = parcFile_Create("/tmp/file_chunker.tmp"); + * PARCRandomAccessFile *a = parcRandomAccessFile_Open(file); + * + * if (parcRandomAccessFile_IsValid(a)) { + * printf("Instance is valid.\n"); + * } + * + * parcRandomAccessFile_Release(&a); + * } + * @endcode + * + */ +PARCRandomAccessFile *parcRandomAccessFile_Open(PARCFile *file); + +/** + * Release a previously acquired reference to the given `PARCRandomAccessFile` instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] instancePtr A pointer to a pointer to the instance to release. + * + * Example: + * @code + * { + * PARCRandomAccessFile *a = parcRandomAccessFile_Open(..); + * + * parcRandomAccessFile_Release(&a); + * } + * @endcode + */ +void parcRandomAccessFile_Release(PARCRandomAccessFile **instancePtr); + +/** + * Create a `PARCJSON` instance (representation) of the given object. + * + * @param [in] instance A pointer to a valid PARCRandomAccessFile instance. + * + * @return NULL Memory could not be allocated to contain the `PARCJSON` instance. + * @return non-NULL An allocated C string that must be deallocated via parcMemory_Deallocate(). + * + * Example: + * @code + * { + * PARCRandomAccessFile *a = parcRandomAccessFile_Open(..); + * + * PARCJSON *json = parcRandomAccessFile_ToJSON(a); + * + * printf("JSON representation: %s\n", parcJSON_ToString(json)); + * parcJSON_Release(&json); + * + * parcRandomAccessFile_Release(&a); + * } + * @endcode + */ +PARCJSON *parcRandomAccessFile_ToJSON(const PARCRandomAccessFile *instance); + +/** + * Produce a null-terminated string representation of the specified `PARCRandomAccessFile`. + * + * The result must be freed by the caller via {@link parcMemory_Deallocate}. + * + * @param [in] instance A pointer to a valid PARCRandomAccessFile instance. + * + * @return NULL Cannot allocate memory. + * @return non-NULL A pointer to an allocated, null-terminated C string that must be deallocated via {@link parcMemory_Deallocate}. + * + * Example: + * @code + * { + * PARCRandomAccessFile *a = parcRandomAccessFile_Open(..); + * + * char *string = parcRandomAccessFile_ToString(a); + * + * parcRandomAccessFile_Release(&a); + * + * parcMemory_Deallocate(&string); + * } + * @endcode + * + * @see parcRandomAccessFile_Display + */ +char *parcRandomAccessFile_ToString(const PARCRandomAccessFile *instance); + +/** + * Close a `PARCRandomAccessFile` instance. + * + * @param [in] fileHandle A `PARCRandomAccessFile.` + * + * @retval true The file was closed successfully. + * @retval false An error occurred. + * + * Example: + * @code + * { + * PARCFile *file = parcFile_Create("/tmp/file_chunker.tmp"); + * PARCRandomAccessFile *handle = parcRandomAccessFile_Open(file); + * + * // use the handle + * + * parcRandomAccessFile_Close(handle); + * parcRandomAccessFile_Release(&handle); + * } + * @endcode + * + * @see parcRandomAccessFile_Open + */ +bool parcRandomAccessFile_Close(PARCRandomAccessFile *fileHandle); + +/** + * Read bytes into the provided `PARCBuffer` until the buffer limit is or the source EOF + * is reached. + * + * @param [in] fileHandle A `PARCRandomAccessFile` from which to read. + * @param [in,out] buffer A `PARCBuffer` into which data is read. + * + * @return The number of bytes read. + * + * Example: + * @code + * { + * PARCFile *file = parcFile_Create("/tmp/file_chunker.tmp"); + * PARCRandomAccessFile *handle = parcRandomAccessFile_Open(file); + * + * PARCBuffer *buffer = parcBuffer_Allocate(1024); + * size_t numBytes = parcRandomAccessFile_Read(handle, buffer); + * + * // use the data in `buffer` + * + * parcRandomAccessFile_Close(handle); + * parcRandomAccessFile_Release(&handle); + * } + * @endcode + * + * @see parcRandomAccessFile_Write + */ +size_t parcRandomAccessFile_Read(PARCRandomAccessFile *fileHandle, PARCBuffer *buffer); + +/** + * Write bytes from the provided `PARCBuffer` to the source file until the limit is reached. + * + * @param [in] fileHandle A `PARCRandomAccessFile` to which data is written. + * @param [in,out] buffer A `PARCBuffer` into which data is read. + * + * @return The number of bytes written. + * + * Example: + * @code + * { + * PARCFile *file = parcFile_Create("/tmp/file_chunker.tmp"); + * PARCRandomAccessFile *handle = parcRandomAccessFile_Open(file); + * + * PARCBuffer *buffer = parcBuffer_WrapCString("important data to go in the file"); + * size_t numBytes = parcRandomAccessFile_Write(handle, buffer); + * + * // continue writing to the file if needed + * + * parcRandomAccessFile_Close(handle); + * parcRandomAccessFile_Release(&handle); + * } + * @endcode + * + * @see parcRandomAccessFile_Read + */ +size_t parcRandomAccessFile_Write(PARCRandomAccessFile *fileHandle, PARCBuffer *buffer); + +/** + * Seek to the position in the file specified as an offset from the position. + * + * The position can be one of: + * PARCRandomAccessFilePosition_Start + * PARCRandomAccessFilePosition_End + * PARCRandomAccessFilePosition_Current + * + * @param [in] fileHandle A `PARCRandomAccessFile` to which data is written. + * @param [in] offset The number of bytes to offset from the provided position. + * @param [in] position The base posititon from which the offset is calculated. + * + * @return The number of bytes seeked. + * + * Example: + * @code + * { + * PARCFile *file = parcFile_Create("/tmp/file_chunker.tmp"); + * PARCRandomAccessFile *handle = parcRandomAccessFile_Open(file); + * + * size_t fileSize = parcFile_GetFileSize(file); + * parcRandomAccessFile_Seek(handle, -1, PARCRandomAccessFilePosition_End); + * + * // use the last byte of the file + * + * parcRandomAccessFile_Close(handle); + * parcRandomAccessFile_Release(&handle); + * } + * @endcode + * + * @see parcRandomAccessFile_Read + */ +size_t parcRandomAccessFile_Seek(PARCRandomAccessFile *fileHandle, long offset, PARCRandomAccessFilePosition position); +#endif diff --git a/libparc/parc/algol/parc_ReadOnlyBuffer.c b/libparc/parc/algol/parc_ReadOnlyBuffer.c new file mode 100755 index 00000000..25628ae8 --- /dev/null +++ b/libparc/parc/algol/parc_ReadOnlyBuffer.c @@ -0,0 +1,245 @@ +/* + * 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 <stdio.h> + +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_Buffer.h> +#include <parc/algol/parc_ReadOnlyBuffer.h> +#include <parc/algol/parc_DisplayIndented.h> + +struct parc_readonly_buffer { + PARCBuffer *buffer; +}; + +static void +_readOnlyBuffer_Finalize(PARCReadOnlyBuffer **bufferPtr) +{ + PARCReadOnlyBuffer *buffer = *bufferPtr; + + parcBuffer_Release(&buffer->buffer); +} + +parcObject_ExtendPARCObject(PARCReadOnlyBuffer, _readOnlyBuffer_Finalize, parcReadOnlyBuffer_Copy, + parcReadOnlyBuffer_ToString, parcReadOnlyBuffer_Equals, NULL, parcReadOnlyBuffer_HashCode, NULL); + +PARCReadOnlyBuffer * +parcReadOnlyBuffer_Create(PARCBuffer *buffer) +{ + PARCReadOnlyBuffer *result = NULL; + if (buffer != NULL) { + result = parcObject_CreateInstance(PARCReadOnlyBuffer); + if (result != NULL) { + result->buffer = parcBuffer_WrapByteArray(parcBuffer_Array(buffer), parcBuffer_Position(buffer), parcBuffer_Limit(buffer)); + } + } + return result; +} + +PARCReadOnlyBuffer * +parcReadOnlyBuffer_Wrap(uint8_t *array, size_t arrayLength, size_t position, size_t limit) +{ + PARCReadOnlyBuffer *result = parcObject_CreateInstance(PARCReadOnlyBuffer); + if (result != NULL) { + result->buffer = parcBuffer_Wrap(array, arrayLength, position, limit); + } + return result; +} + +parcObject_ImplementAcquire(parcReadOnlyBuffer, PARCReadOnlyBuffer); + +parcObject_ImplementRelease(parcReadOnlyBuffer, PARCReadOnlyBuffer); + +size_t +parcReadOnlyBuffer_Capacity(const PARCReadOnlyBuffer *buffer) +{ + return parcBuffer_Capacity(buffer->buffer); +} + +PARCReadOnlyBuffer * +parcReadOnlyBuffer_Clear(PARCReadOnlyBuffer *buffer) +{ + parcBuffer_Clear(buffer->buffer); + return buffer; +} + +bool +parcReadOnlyBuffer_Equals(const PARCReadOnlyBuffer *x, const PARCReadOnlyBuffer *y) +{ + if (x == y) { + return true; + } + if (x == NULL || y == NULL) { + return false; + } + + return parcBuffer_Equals(x->buffer, y->buffer); +} + +PARCByteArray * +parcReadOnlyBuffer_Array(const PARCReadOnlyBuffer *buffer) +{ + return parcBuffer_Array(buffer->buffer); +} + +PARCReadOnlyBuffer * +parcReadOnlyBuffer_Copy(const PARCReadOnlyBuffer *original) +{ + PARCReadOnlyBuffer *result = parcObject_CreateInstance(PARCReadOnlyBuffer); + + if (result != NULL) { + result->buffer = parcBuffer_Copy(original->buffer); + } + return result; +} + +size_t +parcReadOnlyBuffer_ArrayOffset(const PARCReadOnlyBuffer *buffer) +{ + return parcBuffer_ArrayOffset(buffer->buffer); +} + +PARCReadOnlyBuffer * +parcReadOnlyBuffer_Rewind(PARCReadOnlyBuffer *buffer) +{ + parcBuffer_Rewind(buffer->buffer); + return buffer; +} + +PARCReadOnlyBuffer * +parcReadOnlyBuffer_Reset(PARCReadOnlyBuffer *buffer) +{ + parcBuffer_Reset(buffer->buffer); + return buffer; +} + +size_t +parcReadOnlyBuffer_Limit(const PARCReadOnlyBuffer *buffer) +{ + return parcBuffer_Limit(buffer->buffer); +} + +PARCReadOnlyBuffer * +parcReadOnlyBuffer_Mark(PARCReadOnlyBuffer *buffer) +{ + parcBuffer_Mark(buffer->buffer); + return buffer; +} + +PARCReadOnlyBuffer * +parcReadOnlyBuffer_SetLimit(PARCReadOnlyBuffer *buffer, size_t newLimit) +{ + parcBuffer_SetLimit(buffer->buffer, newLimit); + + return buffer; +} + +void * +parcReadOnlyBuffer_Overlay(PARCReadOnlyBuffer *buffer, size_t length) +{ + return parcBuffer_Overlay(buffer->buffer, length); +} + +size_t +parcReadOnlyBuffer_Position(const PARCReadOnlyBuffer *buffer) +{ + return parcBuffer_Position(buffer->buffer); +} + +PARCReadOnlyBuffer * +parcReadOnlyBuffer_SetPosition(PARCReadOnlyBuffer *buffer, size_t newPosition) +{ + parcBuffer_SetPosition(buffer->buffer, newPosition); + return buffer; +} + +size_t +parcReadOnlyBuffer_Remaining(const PARCReadOnlyBuffer *buffer) +{ + return parcBuffer_Remaining(buffer->buffer); +} + +bool +parcReadOnlyBuffer_HasRemaining(const PARCReadOnlyBuffer *buffer) +{ + return parcBuffer_HasRemaining(buffer->buffer); +} + +PARCReadOnlyBuffer * +parcReadOnlyBuffer_Flip(PARCReadOnlyBuffer *buffer) +{ + parcBuffer_Flip(buffer->buffer); + return buffer; +} + +uint8_t +parcReadOnlyBuffer_GetAtIndex(const PARCReadOnlyBuffer *buffer, size_t index) +{ + return parcBuffer_GetAtIndex(buffer->buffer, index); +} + +PARCReadOnlyBuffer * +parcReadOnlyBuffer_GetArray(PARCReadOnlyBuffer *buffer, uint8_t *array, size_t length) +{ + parcBuffer_GetBytes(buffer->buffer, length, array); + return buffer; +} + +uint8_t +parcReadOnlyBuffer_GetUint8(PARCReadOnlyBuffer *buffer) +{ + return parcBuffer_GetUint8(buffer->buffer); +} + +uint16_t +parcReadOnlyBuffer_GetUint16(PARCReadOnlyBuffer *buffer) +{ + return parcBuffer_GetUint16(buffer->buffer); +} + +uint32_t +parcReadOnlyBuffer_GetUint32(PARCReadOnlyBuffer *buffer) +{ + return parcBuffer_GetUint32(buffer->buffer); +} + +uint64_t +parcReadOnlyBuffer_GetUint64(PARCReadOnlyBuffer *buffer) +{ + return parcBuffer_GetUint64(buffer->buffer); +} + +PARCHashCode +parcReadOnlyBuffer_HashCode(const PARCReadOnlyBuffer *buffer) +{ + return parcBuffer_HashCode(buffer->buffer); +} + +char * +parcReadOnlyBuffer_ToString(const PARCReadOnlyBuffer *buffer) +{ + return parcBuffer_ToString(buffer->buffer); +} + +void +parcReadOnlyBuffer_Display(const PARCReadOnlyBuffer *buffer, int indentation) +{ + parcDisplayIndented_PrintLine(indentation, "PARCReadOnlyBuffer@%p {\n", (void *) buffer); + parcBuffer_Display(buffer->buffer, indentation + 1); + parcDisplayIndented_PrintLine(indentation, "}\n"); +} diff --git a/libparc/parc/algol/parc_ReadOnlyBuffer.h b/libparc/parc/algol/parc_ReadOnlyBuffer.h new file mode 100644 index 00000000..39dd6aa8 --- /dev/null +++ b/libparc/parc/algol/parc_ReadOnlyBuffer.h @@ -0,0 +1,834 @@ +/* + * 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. + */ + +/** + * @file parc_ReadOnlyBuffer.h + * @ingroup memory + * @brief An indexable, linear buffer of read-only bytes. + * + * A `PARCReadOnlyBuffer` is a {@link PARCBuffer} that cannot be modified, + * but retains a position, limit and capacity. + * + */ +#ifndef libparc_parc_ReadOnlyBuffer_h +#define libparc_parc_ReadOnlyBuffer_h + +#include <parc/algol/parc_Buffer.h> +#include <parc/algol/parc_ByteArray.h> + +struct parc_readonly_buffer; +typedef struct parc_readonly_buffer PARCReadOnlyBuffer; + +/** + * Create a new instance of `PARCBuPARCReadOnlyBuffer` referencing the content of the given {@link PARCBuffer}. + * + * A reference to the content of the given `PARCBuffer` is acquired. + * + * The new buffer's position, limit, capacity and mark will be the same as the given `PARCBuffer`. + * + * If capacity is zero, the buffer contains no underlying byte array. + * + * @param [in] buffer A pointed to a valid `PARCBuffer` instance. + * + * @return A `PARCReadOnlyBuffer` pointer. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * PARCReadOnlyBuffer *roBuffer = parcReadOnlyBuffer_Create(buffer); + * + * ... + * + * parcReadOnlyBuffer_Release(&roBuffer); + * parcBuffer_Release(&buffer); + * } + * @endcode + */ +PARCReadOnlyBuffer *parcReadOnlyBuffer_Create(PARCBuffer *buffer); + +/** + * Create a new instance of `PARCReadOnlyBuffer` using program supplied memory. + * + * The new buffer will be backed by the given byte array. + * Modifications to the array will be visible through the buffer. + * + * The new buffer's capacity will be @p arrayLength, + * its position will be @p position, + * its limit will be @p limit, + * and its mark will be undefined. + * + * Its backing array will be the given array, + * and its array offset will be zero. + * + * @param [in] array A pointer to a memory array. + * @param [in] arrayLength The length, in `uint8_t` units, of the memory array. + * @param [in] position The initial value for the buffer's position. + * @param [in] limit The initial value for the buffer's limit. + * + * @return A `PARCReadOnlyBuffer` pointer. + * + * Example: + * @code + * { + * uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + * PARCReadOnlyBuffer *roBuffer = parcReadOnlyBuffer_Create(array, 10, 0, 10); + * + * ... + * + * parcReadOnlyBuffer_Release(&roBuffer); + * } + * @endcode + */ +PARCReadOnlyBuffer *parcReadOnlyBuffer_Wrap(uint8_t *array, size_t arrayLength, size_t position, size_t limit); + +/** + * Increase the number of references to a `PARCReadOnlyBuffer`. + * + * Note that new `PARCReadOnlyBuffer` is not created, + * only that the given `PARCReadOnlyBuffer` reference count is incremented. + * Discard the reference by invoking `parcBuffer_Release`. + * + * @param [in] buffer A `PARCReadOnlyBuffer` instance. + * + * @return The input `PARCReadOnlyBuffer` pointer. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * PARCReadOnlyBuffer *roBuffer = parcReadOnlyBuffer_Create(buffer); + * + * PARCReadOnlyBuffer *handle = parcReadOnlyBuffer_Acquire(roBuffer); + * + * ... + * + * parcReadOnlyBuffer_Release(&handle); + * parcReadOnlyBuffer_Release(&roBuffer); + * parcBuffer_Release(&buffer); + * } + * @endcode + */ +PARCReadOnlyBuffer *parcReadOnlyBuffer_Acquire(const PARCReadOnlyBuffer *buffer); + +/** + * Release a `PARCReadOnlyBuffer` reference. + * + * Only the last invocation where the reference count is decremented to zero, + * will actually destroy the `PARCReadOnlyBuffer`. + * + * @param [in,out] bufferPtr Pointer to the `PARCReadOnlyBuffer` instance to be released. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * PARCReadOnlyBuffer *roBuffer = parcReadOnlyBuffer_Create(buffer); + * + * ... + * + * parcReadOnlyBuffer_Release(&roBuffer); + * parcBuffer_Release(&buffer); + * } + * @endcode + */ +void parcReadOnlyBuffer_Release(PARCReadOnlyBuffer **bufferPtr); + +/** + * Returns this buffer's capacity. + * + * @param [in] buffer A pointer to a `PARCReadOnlyBuffer` instance. + * + * @return The given buffer's capacity. + * + * Example: + * @code + * { + * uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + * PARCReadOnlyBuffer *roBuffer = parcReadOnlyBuffer_Create(array, 10, 0, 10); + * + * size_t capacity = parcReadOnlyBuffer_Capacity(roBuffer); + * // capacity will be 10 + * + * ... + * + * parcReadOnlyBuffer_Release(&roBuffer); + * } + * @endcode + */ +size_t parcReadOnlyBuffer_Capacity(const PARCReadOnlyBuffer *buffer); + +/** + * Clear the given buffer: The position is set to zero, + * the limit is set to the capacity, + * and the mark is invalidated. + * + * The mark is made invalid and any subsequent operation on the resulting + * `PARCBuffer` that requires the mark will abort until the mark + * is set again via `parcReadOnlyBuffer_Mark`. + * + * @param [in,out] buffer A pointer to the `PARCReadOnlyBuffer` instance to be modified. + * + * @return The value of @p buffer. + * + * Example: + * @code + * { + * parcReadOnlyBuffer_Clear(buffer); + * } + * @endcode + * + */ +PARCReadOnlyBuffer *parcReadOnlyBuffer_Clear(PARCReadOnlyBuffer *buffer); + +/** + * Determine if two `PARCReadOnlyBuffer` instances are equal. + * + * The following equivalence relations on non-null `PARCReadOnlyBuffer` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, `parcReadOnlyBuffer_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `parcReadOnlyBuffer_Equals(x, y)` must return true if and only if + * `parcReadOnlyBuffer_Equals(y x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcReadOnlyBuffer_Equals(x, y)` returns true and + * `parcReadOnlyBuffer_Equals(y, z)` returns true, + * then `parcReadOnlyBuffer_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `parcReadOnlyBuffer_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `parcReadOnlyBuffer_Equals(x, NULL)` must return false. + * + * + * @param [in] x A pointer to a `PARCReadOnlyBuffer` instance. + * @param [in] y A pointer to a `PARCReadOnlyBuffer` instance. + * + * @return true `PARCReadOnlyBuffer` x and y are equal. + * @return false `PARCReadOnlyBuffer` x and y are not equal. + * + * Example: + * @code + * { + * uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + * PARCReadOnlyBuffer *roBuffer1 = parcReadOnlyBuffer_Create(array, 10, 0, 10); + * PARCReadOnlyBuffer *roBuffer2 = parcReadOnlyBuffer_Create(array, 10, 0, 10); + * + * if (parcReadOnlyBuffer_Equals(roBuffer1, roBuffer2)) { + * printf("ROBuffers are equal\n"); + * } else { + * printf("ROBuffers are NOT equal\n"); + * } + * + * + * parcReadOnlyBuffer_Release(&roBuffer); + * } + * @endcode + */ +bool parcReadOnlyBuffer_Equals(const PARCReadOnlyBuffer *x, const PARCReadOnlyBuffer *y); + +/** + * Return a pointer to the {@link PARCByteArray} that backs this buffer. + * + * If this `PARCReadOnlyBuffer` has a capacity of zero, + * there is no array of bytes and this function returns NULL. + * + * Modifications to the `PARCByteArray` will cause the returned array's content to be modified, and vice versa. + * + * The caller must obtain its own reference to the `PARCByteArray` if it intends to store it elsewhere. + * + * @param [in] buffer A `PARCReadOnlyBuffer` pointer. + * + * @return The pointer to the `PARCByteArray` for the given `PARCReadOnlyBuffer`. + * + * Example: + * @code + * { + * uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + * PARCReadOnlyBuffer *roBuffer = parcReadOnlyBuffer_Create(array, 10, 0, 10); + * + * PARCByteArray *byteArray = parcReadOnlyBuffer_Array(roBuffer); + * + * ... + * + * parcByteArray_Release(&byteArray); + * parcReadOnlyBuffer_Release(&roBuffer); + * } + * @endcode + */ +PARCByteArray *parcReadOnlyBuffer_Array(const PARCReadOnlyBuffer *buffer); + +/** + * Create a copy of the given `PARCReadOnlyBuffer`. + * + * A new buffer is created as a complete copy of the original. + * + * @param [in] original A `PARCReadOnlyBuffer` instance. + * + * @return A `PARCReadOnlyBuffer` instance which is an identical, independent copy of the original. + * + * Example: + * @code + * { + * uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + * PARCReadOnlyBuffer *roBuffer = parcReadOnlyBuffer_Create(array, 10, 0, 10); + * + * PARCReadOnlyBuffer *copy = parcReadOnlyBuffer_Copy(roBuffer); + * + * if (parcReadOnlyBuffer_Equals(roBuffer, copy)) { + * printf("ROBuffers are equal\n"); + * } + * + * parcReadOnlyBuffer_Release(©); + * parcReadOnlyBuffer_Release(&roBuffer); + * } + * @endcode + * + * @see parcReadOnlyBuffer_Equals + */ +PARCReadOnlyBuffer *parcReadOnlyBuffer_Copy(const PARCReadOnlyBuffer *original); + +/** + * Returns the offset within this buffer's backing {@link PARCByteArray} of the first element. + * + * Buffer position <i>p</i> corresponds to array index <i>p + arrayOffset()</i>. + * + * @param [in] buffer A `PARCReadOnlyBuffer` pointer. + * + * @return The offset within this `PARCReadOnlyBuffer`'s array of the first element of the buffer + * + * Example: + * @code + * { + * uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + * PARCReadOnlyBuffer *roBuffer = parcReadOnlyBuffer_Create(array, 10, 0, 10); + * + * size_t arrayOffset = parcReadOnlyBuffer_ArrayOffset(buffer); + * // offset will be 0 since the contents of the buffer start at the beginning + * + * parcReadOnlyBuffer_Release(&roBuffer); + * } + * @endcode + */ +size_t parcReadOnlyBuffer_ArrayOffset(const PARCReadOnlyBuffer *buffer); + +/** + * Rewinds this `PARCReadOnlyBuffer`: The position is set to zero and the mark is invalidated. + * + * The mark is made invalid and any subsequent operation on the resulting + * {@link PARCBuffer} that requires the mark will abort until the mark + * is set again via {@link parcBuffer_Mark}. + * + * @param [in,out] buffer A `PARCReadOnlyBuffer` pointer. + * + * @return The given `PARCReadOnlyBuffer` pointer. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * parcBuffer_PutUint8(buffer, (uint8_t)'A'); + * + * PARCReadOnlyBuffer *roBuffer = parcReadOnlyBuffer_Create(buffer); + * + * parcReadOnlyBuffer_Rewind(roBuffer); + * + * parcReadOnlyBuffer_Release(&roBuffer); + * parcBuffer_Release(&buffer); + * } + * @endcode + */ +PARCReadOnlyBuffer *parcReadOnlyBuffer_Rewind(PARCReadOnlyBuffer *buffer); + +/** + * Resets the given `PARCReadOnlyBuffer`'s position to the previously-marked position. + * + * Invoking this method neither changes nor invalidates the mark's value. + * + * @param [in,out] buffer A `PARCReadOnlyBuffer` pointer. + * + * @return The given `PARCReadOnlyBuffer` pointer. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * parcBuffer_PutUint8(buffer, (uint8_t)'A'); + * + * PARCReadOnlyBuffer *roBuffer = parcReadOnlyBuffer_Create(buffer); + * + * parcReadOnlyBuffer_Reset(roBuffer); + * + * parcReadOnlyBuffer_Release(&roBuffer); + * parcBuffer_Release(&buffer); + * } + * @endcode + */ +PARCReadOnlyBuffer *parcReadOnlyBuffer_Reset(PARCReadOnlyBuffer *buffer); + +/** + * Return the given `PARCReadOnlyBuffer`'s limit. + * + * A buffer's limit is the index of the first element that should not be read or written. + * A buffer's limit is never negative and is never greater than its capacity. + * + * @param [in] buffer A `PARCReadOnlyBuffer` pointer. + * + * @return The given `PARCReadOnlyBuffer`'s limit. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * + * PARCReadOnlyBuffer *roBuffer = parcReadOnlyBuffer_Create(buffer); + * + * size_t limit = parcReadOnlyBuffer_Limit(roBuffer); + * // limit will be 10 + * + * parcReadOnlyBuffer_Release(&roBuffer); + * parcBuffer_Release(&buffer); + * } + * @endcode + */ +size_t parcReadOnlyBuffer_Limit(const PARCReadOnlyBuffer *buffer); + +/** + * Sets this buffer's mark at its position. + * + * @param [in,out] buffer A `PARCReadOnlyBuffer` pointer. + * + * @return The given `PARCReadOnlyBuffer` pointer. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * PARCReadOnlyBuffer *roBuffer = parcReadOnlyBuffer_Create(buffer); + * + * parcReadOnlyBuffer_Mark(roBuffer); + * // since the position was 0, the mark remains at 0 + * + * parcReadOnlyBuffer_Release(&roBuffer); + * parcBuffer_Release(&buffer); + * } + * @endcode + */ +PARCReadOnlyBuffer *parcReadOnlyBuffer_Mark(PARCReadOnlyBuffer *buffer); + +/** + * Sets this `PARCReadOnlyBuffer`'s limit. + * + * If the position is larger than the new limit then it is set to the new limit. + * + * If the mark is defined and larger than the new limit then the mark is invalidated and + * any subsequent operation that requires the mark will abort until the mark + * is set again via {@link parcReadOnlyBuffer_Mark()}. + * + * @param [in] buffer A `PARCReadOnlyBuffer` pointer. + * @param [in] newLimit The new limit value; must be no larger than this `PARCReadOnlyBuffer`'s capacity + * + * @return The given `PARCReadOnlyBuffer` pointer. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * PARCReadOnlyBuffer *roBuffer = parcReadOnlyBuffer_Create(buffer); + * + * parcReadOnlyBuffer_SetLimit(roBuffer, 8); + * // the limit is now 8, but the capacity is 10 + * + * parcReadOnlyBuffer_Release(&roBuffer); + * parcBuffer_Release(&buffer); + * } + * @endcode + */ +PARCReadOnlyBuffer *parcReadOnlyBuffer_SetLimit(PARCReadOnlyBuffer *buffer, size_t newLimit); + +/** + * Return a pointer to memory that can be cast to a specific type. + * + * This does not guarantee proper memory alignment. + * It is possible to obtain a pointer to memory that cannot be accessed + * as integer or other types because of CPU memory alignment requirements. + * + * @param [in,out] buffer A `PARCReadOnlyBuffer` pointer. + * @param [in] length The number of bytes to advance the buffer's position. + * + * @return non-NULL A pointer to memory. + * + * Example: + * @code + * { + * char *expected = "Hello World"; + * struct timeval theTime = { .tv_sec = 123, .tv_usec = 456}; + * + * PARCBuffer *buffer = parcBuffer_Allocate(sizeof(uint16_t) + strlen(expected) + sizeof(theTime)); + * + * parcBuffer_PutUint16(buffer, strlen(expected)); + * parcBuffer_PutUint8(buffer, expected, strlen(expected)); + * parcBuffer_PutUint8(buffer, &theTime, sizeof(theTime)); + * parcBuffer_Flip(); + * + * uint16_t length = parcBuffer_GetUint16(buffer); + * + * PARCReadOnlyBuffer *roBuffer = parcReadOnlyBuffer_Create(buffer); + * + * char *actual = parcReadOnlyBuffer_Overlay(roBuffer, length); + * + * struct timeval *tm = parcReadOnlyBuffer_Overlay(roBuffer, sizeof(struct timeval)); + * } + * @endcode + */ +void *parcReadOnlyBuffer_Overlay(PARCReadOnlyBuffer *buffer, size_t length); + +/** + * Return the given `PARCReadOnlyBuffer`'s position. + * + * A buffer's position is the index of the next element to be read or written. + * A buffer's position is never negative and is never greater than its limit. + * + * @param [in] buffer A `PARCReadOnlyBuffer` pointer. + * + * @return The given `PARCReadOnlyBuffer`'s position. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * PARCReadOnlyBuffer *roBuffer = parcReadOnlyBuffer_Create(buffer); + * + * size_t position = parcReadOnlyBuffer_Position(roBuffer); + * // position is zero since the position of the underlying buffer is also 0 + * + * parcReadOnlyBuffer_Release(&roBuffer); + * parcBuffer_Release(&buffer); + * } + * @endcode + */ +size_t parcReadOnlyBuffer_Position(const PARCReadOnlyBuffer *buffer); + +/** + * Set the given `PARCReadOnlyBuffer`'s position. + * + * A buffer's position is the index of the next element to be read or written. + * A buffer's position is never negative and is never greater than its limit. + * + * If the mark is defined and larger than the new position then the mark + * is invalidated and any subsequent operation on the resulting + * `PARCReadOnlyBuffer` that requires the mark will abort until the mark + * is set again via `parcReadOnlyBuffer_Mark`. + * + * @param [in,out] buffer A `PARCReadOnlyBuffer` pointer. + * @param [in] newPosition The value of the new position which must be less than or equal to the buffer's current limit. + * + * @return The given `PARCReadOnlyBuffer`'s position. + * + * Example: + * @code + * { + * PARCReadOnlyBuffer *buffer = parcReadOnlyBuffer_Create(parcBuffer_Allocate(10)); + * parcReadOnlyBuffer_SetPosition(buffer, 5); + * parcReadOnlyBuffer_Remaining(buffer); // Returns 5. + * } + * @endcode + */ +PARCReadOnlyBuffer *parcReadOnlyBuffer_SetPosition(PARCReadOnlyBuffer *buffer, size_t newPosition); + +/** + * Returns the number of elements between the current position and the limit. + * + * @param [in] buffer A `PARCReadOnlyBuffer` pointer. + * + * @return The number of elements remaining in this `PARCReadOnlyBuffer`. + * + * Example: + * @code + * { + * parcReadOnlyBuffer_SetPosition(buffer, 5); + * parcReadOnlyBuffer_Remaining(buffer); // Returns 5. + * } + * @endcode + */ +size_t parcReadOnlyBuffer_Remaining(const PARCReadOnlyBuffer *buffer); + +/** + * Tells whether there are any elements between the current position and the limit. + * + * @param [in] buffer A `PARCReadOnlyBuffer` pointer. + * + * @return true if, and only if, there is at least one element remaining in this `PARCReadOnlyBuffer`. + * + * Example: + * @code + * { + * parcReadOnlyBuffer_SetPosition(buffer, 5); + * bool remaining = parcReadOnlyBuffer_HasRemaining(buffer); + * // remaining is true, since #remaining = 5 + * } + * @endcode + */ +bool parcReadOnlyBuffer_HasRemaining(const PARCReadOnlyBuffer *buffer); + +/** + * Set the limit to the current position, then set the position to zero. + * If the mark is defined, it is invalidated. + * + * @param [in,out] buffer The `PARCReadOnlyBuffer` pointer to be modified. + * + * @return The same value as `buffer` + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * parcBuffer_PutByte(buffer, 'X'); + * PARCReadOnlyBuffer *roBuffer = parcReadOnlyBuffer_Create(buffer); + * + * parcReadOnlyBuffer_Flip(roBuffer); + * uint8_t actual = parcReadOnlyBuffer_GetUint8(roBuffer); + * + * ... + * + * parcReadOnlyBuffer_Release(&roBuffer); + * parcBuffer_Release(&buffer); + * } + */ +PARCReadOnlyBuffer *parcReadOnlyBuffer_Flip(PARCReadOnlyBuffer *buffer); + +/** + * Get the single uint8_t at the index specified. + * + * @param [in] buffer A `PARCReadOnlyBuffer` pointer. + * @param [in] index The index from which to retrieve the single byte in the buffer. + * + * @return The uint8_t value at the specified index + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * parcBuffer_PutByte(buffer, 'X'); + * PARCReadOnlyBuffer *roBuffer = parcReadOnlyBuffer_Create(buffer); + * + * uint8_t byte = parcReadOnlyBuffer_GetAtIndex(roBuffer, 0); + * + * ... + * + * parcReadOnlyBuffer_Release(&roBuffer); + * parcBuffer_Release(&buffer); + * } + */ +uint8_t parcReadOnlyBuffer_GetAtIndex(const PARCReadOnlyBuffer *buffer, size_t index); + +/** + * Read an array of length bytes from the given PARCReadOnlyBuffer, copying them to an array. + * + * The buffer's position is incremented by @p length. + * + * @param [in] buffer The `PARCReadOnlyBuffer` containing the `uint8_t` value. + * @param [out] array The `uint8_t` array to receive @p length bytes. + * @param [in] length The number of `uint8_t` elements to get.. + * + * @return The given `PARCReadOnlyBuffer`. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(5); + * parcBuffer_PutUint8(buffer, 'A'); + * parcBuffer_PutUint8(buffer, 'B'); + * parcBuffer_PutUint8(buffer, 'C'); + * PARCReadOnlyBuffer *roBuffer = parcReadOnlyBuffer_Create(buffer); + * + * uint8_t array[3]; + * parcReadOnlyBuffer_GetArray(roBuffer, 3, array); + * // array[0] == 'A' + * // array[1] == 'B' + * // array[2] == 'C' + * } + * @endcode + * + * @see parcReadOnlyBuffer_Overlay + */ +PARCReadOnlyBuffer *parcReadOnlyBuffer_GetArray(PARCReadOnlyBuffer *buffer, uint8_t *array, size_t length); + +/** + * Get the single `uint8_t` at the current buffer position. + * + * @param [in] buffer A `PARCReadOnlyBuffer` pointer. + * + * @return The `uint8_t` value at the current position. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * parcBuffer_PutByte(buffer, 'X'); + * PARCReadOnlyBuffer *roBuffer = parcReadOnlyBuffer_Create(buffer); + * parcReadOnlyBuffer_Flip(roBuffer); + * + * uint8_t actual = parcReadOnlyBuffer_GetUint8(roBuffer); + * + * parcReadOnlyBuffer_Release(&roBuffer); + * parcBuffer_Release(&buffer); + * } + * @endcode + */ +uint8_t parcReadOnlyBuffer_GetUint8(PARCReadOnlyBuffer *buffer); + +/** + * Get the single `uint16_t` at the current buffer position. + * + * @param [in] buffer A `PARCReadOnlyBuffer` pointer. + * + * @return The `uint16_t` value at the current position. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * parcBuffer_PutUint16(buffer, 0x1234); + * PARCReadOnlyBuffer *roBuffer = parcReadOnlyBuffer_Create(buffer); + * parcReadOnlyBuffer_Flip(roBuffer); + * + * uint16_t actual = parcReadOnlyBuffer_GetUint16(roBuffer); + * + * parcReadOnlyBuffer_Release(&roBuffer); + * parcBuffer_Release(&buffer); + * } + * @endcode + */ +uint16_t parcReadOnlyBuffer_GetUint16(PARCReadOnlyBuffer *buffer); + +/** + * Get the single `uint32_t` at the current buffer position. + * + * @param [in] buffer A `PARCReadOnlyBuffer` pointer. + * + * @return The `uint32_t` value at the current position. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(10); + * parcBuffer_PutUint32(buffer, 0x12345678); + * PARCReadOnlyBuffer *roBuffer = parcReadOnlyBuffer_Create(buffer); + * parcReadOnlyBuffer_Flip(roBuffer); + * + * uint32_t actual = parcReadOnlyBuffer_GetUint32(roBuffer); + * + * parcReadOnlyBuffer_Release(&roBuffer); + * parcBuffer_Release(&buffer); + * } + * @endcode + */ +uint32_t parcReadOnlyBuffer_GetUint32(PARCReadOnlyBuffer *buffer); + +/** + * Read the unsigned 64-bit value in network order at the buffer's current position, + * and then increment the position by 8. + * + * @param [in,out] buffer The `PARCReadOnlyBuffer` containing the value. + * + * @return The `uint64_t` at the buffer's current position. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Allocate(sizeof(uint64_t)); + * parcBuffer_PutUint64(buffer, 0x1234567812345678); + * parcBuffer_Flip(buffer); + * + * PARCReadOnlyBuffer *readOnly = parcReadOnlyBuffer_Create(buffer); + * uint64_t actual = parcReadOnlyBuffer_GetUint64(&readOnly); + * } + * @endcode + */ +uint64_t parcReadOnlyBuffer_GetUint64(PARCReadOnlyBuffer *buffer); + +/** + * Returns a hash code value for the given instance. + * + * The general contract of `HashCode` is: + * + * Whenever it is invoked on the same instance more than once during an execution of an application, + * the {@link parcReadOnlyBuffer_HashCode} function must consistently return the same value, + * provided no information used in a corresponding {@link parcReadOnlyBuffer_Equals()} + * comparisons on the instance is modified. + * + * This value need not remain consistent from one execution of an application to another execution of the same application. + * If two instances are equal according to the {@link parcReadOnlyBuffer_Equals} method, + * then calling the {@link parcReadOnlyBuffer_HashCode} method on each of the two instances must produce the same integer result. + * + * It is not required that if two instances are unequal according to the {@link parcReadOnlyBuffer_Equals()} function, + * then calling the {@link parcReadOnlyBuffer_HashCode()} + * method on each of the two objects must produce distinct integer results. + * + * @param [in] buffer A pointer to the `PARCReadOnlyBuffer` instance. + * + * @return The hashcode for the given instance. + * + * Example: + * @code + * { + * uint32_t hashValue = parcReadOnlyBuffer_HashCode(buffer); + * } + * @endcode + * + * @see parcReadOnlyBuffer_Equals + */ +PARCHashCode parcReadOnlyBuffer_HashCode(const PARCReadOnlyBuffer *buffer); + +/** + * Create a null-terminated C string containing the bytes of the given `PARCReadOnlyBuffer`. + * + * The string consists of the bytes from the current position of the buffer to its limit. + * + * @param [in] buffer A `PARCReadOnlyBuffer` instance. + * + * @return NULL Cannot allocate memory. + * @return non-NULL A pointer to a null-terminated C string that must be deallocated via {@link parcMemory_Deallocate}. + * + * Example: + * @code + * { + * char *string = parcReadOnlyBuffer_ToString(buffer); + * + * parcMemory_Deallocate((void **)&string); + * } + * @endcode + */ +char *parcReadOnlyBuffer_ToString(const PARCReadOnlyBuffer *buffer); + +/** + * Print a human readable representation of the given `PARCReadOnlyBuffer`. + * + * Print on standard output a human readable representation of the given `PARCReadOnlyBuffer` indented by the level indicated. + * + * @param [in] buffer A pointer to the `PARCReadOnlyBuffer` instance. + * @param [in] indentation The number of tabs by which to indent the output strings. + * + * Example: + * @code + * { + * parcReadOnlyBuffer_Display(buffer, 0); + * } + * @endcode + */ +void parcReadOnlyBuffer_Display(const PARCReadOnlyBuffer *buffer, int indentation); +#endif // libparc_parc_ReadOnlyBuffer_h diff --git a/libparc/parc/algol/parc_SafeMemory.c b/libparc/parc/algol/parc_SafeMemory.c new file mode 100644 index 00000000..1f152af9 --- /dev/null +++ b/libparc/parc/algol/parc_SafeMemory.c @@ -0,0 +1,658 @@ +/* + * 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. + */ + +/** + * This is a substitute for posix_memalign(3) that + * establishes detectable boundaries around an allocated memory segment, + * records a stack backtrace for each allocation, + * detects buffer overruns and underruns by checking the boundaries when the memory is deallocated, + * and tries to prevent a stray pointer to reference the memory again once it's been deallocated. + * + * The allocated memory consists of three contiguous segments: the prefix, the memory usable by the caller, and the suffix. + * The memory usable by the caller is aligned as specified by the caller. + * The alignment must be a power of 2 greater than or equal to the size of a {@code void *}. + * <pre> + * +--base +-prefix +-- memory +-- suffix aligned on (void *) + * v v v v + * |________|PPPPPPPPPPPP|mmmmmmmmm...mmmm|___|SSSSSSSSS + * ^ + * +-- variable padding + * </pre> + * Where '-' indicates padding, 'P' indicates the prefix data structure, 'm' + * indicates contiguous memory for use by the caller, and 'S" indicates the suffix data structure. + * + */ +#include <config.h> + +#include <LongBow/runtime.h> + +#if defined(_WIN64) +# define backtrace(...) (0) +# define backtrace_symbols(...) 0 +# define backtrace_symbols_fd(...) ((void) 0) +#elif defined(_WIN32) +# define backtrace(...) (0) +# define backtrace_symbols(...) 0 +# define backtrace_symbols_fd(...) ((void) 0) +#elif defined(__ANDROID__) +# define backtrace(...) (0) +# define backtrace_symbols(...) 0 +# define backtrace_symbols_fd(...) ((void) 0) +#elif defined(__APPLE__) +# include <execinfo.h> +#elif defined(__linux) +# include <execinfo.h> +#elif defined(__unix) // all unices not caught above +# define backtrace(...) (0) +# define backtrace_symbols(...) 0 +# define backtrace_symbols_fd(...) ((void) 0) +#elif defined(__posix) +# include <execinfo.h> +#endif + +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <sys/errno.h> +#include <sys/queue.h> +#include <pthread.h> + +#include <stdint.h> +#include <inttypes.h> +#include <stdbool.h> + +#include <parc/algol/parc_StdlibMemory.h> +#include <parc/algol/parc_SafeMemory.h> +#include <parc/algol/parc_DisplayIndented.h> + +typedef struct memory_backtrace { + void **callstack; + int maximumFrameCount; + int actualFrameCount; +} _MemoryBacktrace; + +static const uint32_t _parcSafeMemory_SuffixGuard = 0xcafecafe; +typedef struct memory_suffix { + uint32_t guard; +} _MemorySuffix; + +static const uint64_t _parcSafeMemory_PrefixMagic = 0xfacefacefaceface; +static const uint64_t _parcSafeMemory_Guard = 0xdeaddeaddeaddead; +static const uint64_t _parcSafeMemory_GuardAlreadyFreed = 0xBADDCAFEBADDCAFE; +typedef struct memory_prefix { + uint64_t magic; // A magic number indicating the start of this data structure. + size_t requestedLength; // The number of bytes the caller requested. + size_t actualLength; // The number of bytes >= requestedLength to ensure the right alignment for the suffix. + size_t alignment; // The aligment required by the caller. Must be a power of 2 and >= sizeof(void *). + _MemoryBacktrace *backtrace; // A record of the caller's stack trace at the time of allocation. + uint64_t guard; // Try to detect underrun of the allocated memory. +} _MemoryPrefix; + +typedef void *PARCSafeMemoryOrigin; + +typedef void *PARCSafeMemoryUsable; + +static PARCMemoryInterface *_parcMemory = &PARCStdlibMemoryAsPARCMemory; + +static pthread_mutex_t _parcSafeMemory_Mutex = PTHREAD_MUTEX_INITIALIZER; + + +/** + * Return true if the given alignment value is greater than or equal to {@code sizeof(void *)} and + * is a power of 2. + * + * @param alignment + * @return + */ +static bool +_alignmentIsValid(size_t alignment) +{ + return alignment >= sizeof(void *) && (alignment & (~alignment + 1)) == alignment; +} + +/** + * Add two pointers arithmetically. + */ +// Should increment be a ssize_t, instead of size_t? +static void * +_pointerAdd(const void *base, const size_t increment) +{ + void *result = (void *) &((char *) base)[increment]; + return result; +} + +static size_t +_computePrefixLength(const size_t alignment) +{ + return (sizeof(_MemoryPrefix) + alignment - 1) & ~(alignment - 1); +} + +static size_t +_computeUsableMemoryLength(const size_t requestedLength, const size_t alignment) +{ + return (requestedLength + alignment - 1) & ~(alignment - 1); +} + +/** + * Compute the size of the suffix on an allocated chunk of managed memory that causes the + * first byte after this size to be aligned according to the given alignment value. + */ +static size_t +_computeSuffixLength(size_t alignment __attribute__((unused))) +{ + return sizeof(_MemorySuffix); +} + +/** + * Compute the total number of bytes necessary to store the entire Safe Memory structure. + * Given a size number of bytes for use by a client function, + * produce the total number of bytes necessary to store @p size + * number of bytes for use by a client function and the `_MemoryPrefix` + * and `_MemorySuffix` structures. + */ +static size_t +_computeMemoryTotalLength(size_t requestedLength, size_t alignment) +{ + size_t result = + _computePrefixLength(alignment) + + _computeUsableMemoryLength(requestedLength, sizeof(void*)) + + _computeSuffixLength(alignment); + + return result; +} + +/** + * Given the safe memory address, return a pointer to the _MemoryPrefix structure. + */ +static _MemoryPrefix * +_parcSafeMemory_GetPrefix(const PARCSafeMemoryUsable *usable) +{ + _MemoryPrefix *prefix = _pointerAdd(usable, -sizeof(_MemoryPrefix)); + return prefix; +} + +/** + * Given a base address for memory Return a pointer to the {@link _MemorySuffix} + * structure for the given base pointer to allocated memory. + * + * @param base + * @return + */ +static _MemorySuffix * +_parcSafeMemory_GetSuffix(const PARCSafeMemoryUsable *memory) +{ + _MemoryPrefix *prefix = _parcSafeMemory_GetPrefix(memory); + + _MemorySuffix *suffix = _pointerAdd(memory, prefix->actualLength); + return suffix; +} + +static PARCSafeMemoryState +_parcSafeMemory_GetPrefixState(const PARCSafeMemoryUsable *usable) +{ + _MemoryPrefix *prefix = _parcSafeMemory_GetPrefix(usable); + + if (prefix->guard == _parcSafeMemory_GuardAlreadyFreed) { + return PARCSafeMemoryState_ALREADYFREE; + } + if (prefix->guard != _parcSafeMemory_Guard) { + return PARCSafeMemoryState_UNDERRUN; + } + if (prefix->magic != _parcSafeMemory_PrefixMagic) { + return PARCSafeMemoryState_UNDERRUN; + } + if (!_alignmentIsValid(prefix->alignment)) { + return PARCSafeMemoryState_UNDERRUN; + } + + return PARCSafeMemoryState_OK; +} + +static PARCSafeMemoryOrigin * +_parcSafeMemory_GetOrigin(const PARCSafeMemoryUsable *memory) +{ + _MemoryPrefix *prefix = _parcSafeMemory_GetPrefix(memory); + + return _pointerAdd(memory, -_computePrefixLength(prefix->alignment)); +} + +static PARCSafeMemoryState +_parcSafeMemory_GetSuffixState(const PARCSafeMemoryUsable *memory) +{ + PARCSafeMemoryState result = PARCSafeMemoryState_OK;; + _MemorySuffix *suffix = _parcSafeMemory_GetSuffix(memory); + if (suffix->guard != _parcSafeMemory_SuffixGuard) { + result = PARCSafeMemoryState_OVERRUN; + } + return result; +} + +/** + * Given a pointer to the base address of an allocated memory segment, + * compute and return a pointer to the corresponding {@link _MemorySuffix} for that same memory. + */ +static _MemorySuffix * +_parcSafeMemory_FormatSuffix(const PARCSafeMemoryUsable *memory) +{ + _MemorySuffix *suffix = _parcSafeMemory_GetSuffix(memory); + + suffix->guard = _parcSafeMemory_SuffixGuard; + return suffix; +} + +static void +_backtraceReport(const _MemoryBacktrace *backtrace, int outputFd) +{ + if (outputFd != -1) { + // Ignore the first entry as it points to this function and we just need to start at the calling function. + backtrace_symbols_fd(&backtrace->callstack[1], backtrace->actualFrameCount - 1, outputFd); + } +} + +// This is a list of all memory allocations that were created by calls to Safe Memory. +// Each element of the list is the pointer to the result returned to the caller of the memory allocation, +// not a pointer to the base. +struct safememory_entry { + LIST_ENTRY(safememory_entry) entries; // List + void *memory; +}; +LIST_HEAD(, safememory_entry) head = LIST_HEAD_INITIALIZER(head); +static pthread_mutex_t head_mutex = PTHREAD_MUTEX_INITIALIZER; + +static void +_parcSafeMemory_AddAllocation(void *memory) +{ + if (parcSafeMemory_Outstanding() == 0) { + LIST_INIT(&head); // Initialize the list. + } + + struct safememory_entry *e = malloc(sizeof(struct safememory_entry)); // Insert this at the head. + e->memory = memory; + + pthread_mutex_lock(&head_mutex); + LIST_INSERT_HEAD(&head, e, entries); + pthread_mutex_unlock(&head_mutex); +} + +static void +_parcSafeMemory_RemoveAllocation(void *memory) +{ + struct safememory_entry *e; + pthread_mutex_lock(&head_mutex); + LIST_FOREACH(e, &head, entries) + { + if (e->memory == memory) { + LIST_REMOVE(e, entries); + free(e); + pthread_mutex_unlock(&head_mutex); + return; + } + } + + pthread_mutex_unlock(&head_mutex); + fprintf(stderr, "parcSafeMemory_RemoveAllocation: Destroying memory (%p) which is NOT in the allocated memory record. Double free?\n", memory); +} + +static PARCSafeMemoryState +_parcSafeMemory_GetState(const PARCSafeMemoryUsable *memory) +{ + PARCSafeMemoryState prefixState = _parcSafeMemory_GetPrefixState(memory); + if (prefixState != PARCSafeMemoryState_OK) { + return prefixState; + } + return _parcSafeMemory_GetSuffixState(memory); +} + +static const char * +_parcSafeMemory_StateToString(PARCSafeMemoryState status) +{ + switch (status) { + case PARCSafeMemoryState_OK: return "OK"; + case PARCSafeMemoryState_MISMATCHED: return "MISMATCHED"; + case PARCSafeMemoryState_UNDERRUN: return "UNDERRUN"; + case PARCSafeMemoryState_OVERRUN: return "OVERRUN"; + case PARCSafeMemoryState_NOTHINGALLOCATED: return "NOTHINGALLOCATED"; + case PARCSafeMemoryState_ALREADYFREE: return "ALREADYFREE"; + } + return "?"; +} + +static void +_parcSafeMemory_Report(const PARCSafeMemoryUsable *safeMemory, int outputFd) +{ + _MemoryPrefix *prefix = _parcSafeMemory_GetPrefix(safeMemory); + + if (outputFd != -1) { + int charactersPrinted = dprintf(outputFd, "Memory %p (base %p) %s\n", + (void *) safeMemory, + (void *) prefix, + _parcSafeMemory_StateToString(_parcSafeMemory_GetState(safeMemory))); + trapUnexpectedStateIf(charactersPrinted < 0, "Cannot write to file descriptor %d", outputFd); + } + _backtraceReport(prefix->backtrace, outputFd); +} + +uint32_t +parcSafeMemory_ReportAllocation(int outputFd) +{ + uint32_t index = 0; + struct safememory_entry *e; + + pthread_mutex_lock(&head_mutex); + LIST_FOREACH(e, &head, entries) + { + _MemoryPrefix *prefix = _parcSafeMemory_GetPrefix(e->memory); + if (outputFd != -1) { + int charactersPrinted = dprintf(outputFd, + "\n%u SafeMemory@%p: %p={ .requestedLength=%zd, .actualLength=%zd, .alignment=%zd }\n", + index, e->memory, (void *) prefix, prefix->requestedLength, prefix->actualLength, prefix->alignment); + trapUnexpectedStateIf(charactersPrinted < 0, "Cannot write to file descriptor %d", outputFd) + { + pthread_mutex_unlock(&head_mutex); + } + } + _parcSafeMemory_Report(e->memory, outputFd); + index++; + } + pthread_mutex_unlock(&head_mutex); + return parcSafeMemory_Outstanding(); +} + +static void +_backtraceDestroy(_MemoryBacktrace **backtrace) +{ + free((*backtrace)->callstack); + free(*backtrace); + *backtrace = 0; +} + +static PARCSafeMemoryState +_parcSafeMemory_Destroy(void **memoryPointer) +{ + pthread_mutex_lock(&_parcSafeMemory_Mutex); + + if (parcSafeMemory_Outstanding() == 0) { + pthread_mutex_unlock(&_parcSafeMemory_Mutex); + return PARCSafeMemoryState_NOTHINGALLOCATED; + } + + _parcSafeMemory_RemoveAllocation(*memoryPointer); + + PARCSafeMemoryUsable *memory = *memoryPointer; + + PARCSafeMemoryState state = _parcSafeMemory_GetState(memory); + trapUnexpectedStateIf(state != PARCSafeMemoryState_OK, + "Expected PARCSafeMemoryState_OK, actual %s (see parc_SafeMemory.h)", + _parcSafeMemory_StateToString(state)) + { + pthread_mutex_unlock(&_parcSafeMemory_Mutex); + } + + _MemoryPrefix *prefix = _parcSafeMemory_GetPrefix(memory); + _backtraceDestroy(&prefix->backtrace); + + PARCSafeMemoryOrigin *base = _parcSafeMemory_GetOrigin(memory); + + memset(base, 0, _computeMemoryTotalLength(prefix->requestedLength, prefix->alignment)); + prefix->guard = _parcSafeMemory_GuardAlreadyFreed; + + ((PARCMemoryDeallocate *) _parcMemory->Deallocate)((void **) &base); + + *memoryPointer = 0; + + pthread_mutex_unlock(&_parcSafeMemory_Mutex); + return PARCSafeMemoryState_OK; +} + +__attribute__((unused)) +static void +_parcSafeMemory_DeallocateAll(void) +{ + struct safememory_entry *e; + + pthread_mutex_lock(&head_mutex); + LIST_FOREACH(e, &head, entries) + { + _parcSafeMemory_Destroy(&e->memory); + } + pthread_mutex_unlock(&head_mutex); +} + +static _MemoryBacktrace * +_backtraceCreate(int maximumFrameCount) +{ + _MemoryBacktrace *result = malloc(sizeof(_MemoryBacktrace)); + result->maximumFrameCount = maximumFrameCount; + result->callstack = calloc(result->maximumFrameCount, sizeof(void *)); + + result->actualFrameCount = backtrace(result->callstack, result->maximumFrameCount); + + return result; +} + +/** + * Format memory with a MemoryPrefix structure. + * + * @param origin The origin of the allocated memory (which is not the same as the start of usable memory). + * @param requestedLength The length of the extent of memory for general purpose use by the caller. + * @param alignment A power of 2 greater than or equal to {@code sizeof(void *)}. + * @return The pointer to the first address suitable for general purpose use by the caller. + */ +static PARCSafeMemoryUsable * +_parcSafeMemory_FormatPrefix(PARCSafeMemoryOrigin *origin, size_t requestedLength, size_t alignment) +{ + int backTraceDepth = 20; + + if (!_alignmentIsValid(alignment)) { + return NULL; + } + size_t prefixSize = _computePrefixLength(alignment); + + // This abuts the prefix to the user memory, it does not start at the beginning + // of the aligned prefix region. + _MemoryPrefix *prefix = (_MemoryPrefix *) (origin + (prefixSize - sizeof(_MemoryPrefix))); + + prefix->magic = _parcSafeMemory_PrefixMagic; + prefix->requestedLength = requestedLength; + prefix->actualLength = _computeUsableMemoryLength(requestedLength, sizeof(void*)); + prefix->alignment = alignment; + prefix->backtrace = _backtraceCreate(backTraceDepth); + prefix->guard = _parcSafeMemory_Guard; + + PARCSafeMemoryUsable *result = _pointerAdd(origin, prefixSize); + + assertAligned(result, alignment, "Return value is not properly aligned to %zu", alignment); + return result; +} + +/** + * Given a pointer to allocated memory and the length of bytes that will be used by the caller, + * format the prefix and suffix structures returning a pointer to the first properly aligned + * byte available to the client function. + */ +static void * +_parcSafeMemory_FormatMemory(PARCSafeMemoryOrigin *origin, size_t length, size_t alignment) +{ + PARCSafeMemoryUsable *memory = _parcSafeMemory_FormatPrefix(origin, length, alignment); + if (memory != NULL) { + _parcSafeMemory_FormatSuffix(memory); + } + + return memory; +} + +int +parcSafeMemory_MemAlign(void **memptr, size_t alignment, size_t requestedSize) +{ + if (!_alignmentIsValid(alignment)) { + return EINVAL; + } + if (requestedSize == 0) { + return EINVAL; + } + + size_t totalSize = _computeMemoryTotalLength(requestedSize, alignment); + if (totalSize < requestedSize) { + return ERANGE; + } + + pthread_mutex_lock(&_parcSafeMemory_Mutex); + + void *base; + int failure = ((PARCMemoryMemAlign *) _parcMemory->MemAlign)(&base, alignment, totalSize); + + if (failure != 0 || base == NULL) { + pthread_mutex_unlock(&_parcSafeMemory_Mutex); + return ENOMEM; + } + + *memptr = _parcSafeMemory_FormatMemory(base, requestedSize, alignment); + + _parcSafeMemory_AddAllocation(*memptr); + pthread_mutex_unlock(&_parcSafeMemory_Mutex); + + return 0; +} + +void * +parcSafeMemory_Allocate(size_t requestedSize) +{ + void *result = NULL; + + if (requestedSize != 0) { + size_t totalSize = _computeMemoryTotalLength(requestedSize, sizeof(void *)); + + if (totalSize >= requestedSize) { + pthread_mutex_lock(&_parcSafeMemory_Mutex); + + void *base = ((PARCMemoryAllocate *) _parcMemory->Allocate)(totalSize); + if (base != NULL) { + result = _parcSafeMemory_FormatMemory(base, requestedSize, sizeof(void *)); + + _parcSafeMemory_AddAllocation(result); + } + pthread_mutex_unlock(&_parcSafeMemory_Mutex); + } + } + return result; +} + +void * +parcSafeMemory_AllocateAndClear(size_t requestedSize) +{ + void *memptr = parcSafeMemory_Allocate(requestedSize); + if (memptr != NULL) { + memset(memptr, 0, requestedSize); + } + return memptr; +} + +bool +parcSafeMemory_IsValid(const void *memory) +{ + bool result = true; + + PARCSafeMemoryState state = _parcSafeMemory_GetState(memory); + if (state != PARCSafeMemoryState_OK) { + return false; + } + + return result; +} + + +uint32_t +parcSafeMemory_Outstanding(void) +{ + return ((PARCMemoryOutstanding *) _parcMemory->Outstanding)(); +} + +void * +parcSafeMemory_Reallocate(void *original, size_t newSize) +{ + void *result; + + result = parcSafeMemory_Allocate(newSize); + + if (original == NULL) { + return result; + } + + if (result != NULL) { + _MemoryPrefix *prefix = _parcSafeMemory_GetPrefix(original); + size_t originalSize = prefix->requestedLength; + + memcpy(result, original, originalSize); + parcSafeMemory_Deallocate(&original); + } + return result; +} + +char * +parcSafeMemory_StringDuplicate(const char *string, size_t length) +{ + size_t actualLength = strlen(string); + if (length < actualLength) { + actualLength = length; + } + + char *result = parcSafeMemory_Allocate(actualLength + 1); + if (result != NULL) { + memcpy(result, string, actualLength); + result[actualLength] = 0; + } + return result; +} + +void +parcSafeMemory_Deallocate(void **pointer) +{ + _parcSafeMemory_Destroy(pointer); +} + +void +parcSafeMemory_Display(const void *memory, int indentation) +{ + if (memory == NULL) { + parcDisplayIndented_PrintLine(indentation, "PARCSafeMemory@NULL"); + } else { + _MemoryPrefix *prefix = _parcSafeMemory_GetPrefix(memory); + + parcDisplayIndented_PrintLine(indentation, "PARCSafeMemory@%p {", (void *) memory); + parcDisplayIndented_PrintLine(indentation + 1, + "%p=[ magic=0x%" PRIx64 " requestedLength=%zd, actualLength=%zd, alignment=%zd, guard=0x%" PRIx64 "]", + _parcSafeMemory_GetOrigin(memory), + prefix->magic, + prefix->requestedLength, + prefix->actualLength, + prefix->alignment, + prefix->guard); + + parcDisplayIndented_PrintMemory(indentation + 1, prefix->requestedLength, memory); + + parcDisplayIndented_PrintLine(indentation, "}"); + } +} + +PARCMemoryInterface PARCSafeMemoryAsPARCMemory = { + .Allocate = (uintptr_t) parcSafeMemory_Allocate, + .AllocateAndClear = (uintptr_t) parcSafeMemory_AllocateAndClear, + .MemAlign = (uintptr_t) parcSafeMemory_MemAlign, + .Deallocate = (uintptr_t) parcSafeMemory_Deallocate, + .Reallocate = (uintptr_t) parcSafeMemory_Reallocate, + .Outstanding = (uintptr_t) parcSafeMemory_Outstanding, + .StringDuplicate = (uintptr_t) parcSafeMemory_StringDuplicate +}; diff --git a/libparc/parc/algol/parc_SafeMemory.h b/libparc/parc/algol/parc_SafeMemory.h new file mode 100644 index 00000000..0383fe39 --- /dev/null +++ b/libparc/parc/algol/parc_SafeMemory.h @@ -0,0 +1,335 @@ +/* + * 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. + */ + +/** + * @file parc_SafeMemory.h + * @ingroup developer + * + * @brief Facade to memory functions to make calls safer. + * + * This is a facade to interpose between an application and the standard C library functions posix_memalign(3), + * malloc(3), calloc(3), realloc(3) and free(3) that establishes detectable boundaries around an allocated memory segment, + * records a stack backtrace for each allocation, + * detects buffer overruns and underruns by checking the boundaries when the memory is deallocated, + * and tries to prevent a stray pointer to reference the memory again once it's been deallocated. + * + * The allocated memory consists of three contiguous segments: the prefix, the memory usable by the caller, and the suffix. + * The memory usable by the caller is aligned as specified by the caller. + * The alignment must be a power of 2 greater than or equal to the size of a {@code void *}. + * <pre> + * +--base +-prefix +-- aligned memory +-- suffix aligned on (void *) + * v v v v + * |________|PPPPPPPPPPPP|mmmmmmmmm...mmmm|___|SSSSSSSSS + * ^ + * +-- variable padding + * </pre> + * Where '-' indicates padding, 'P' indicates the prefix data structure, 'm' + * indicates contiguous memory for use by the caller, and 'S" indicates the suffix data structure. + * + * To enable this facade, you must include the following line in your execution before any allocations are performed. + * + * @code + * { + * parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + * } + * @endcode + * + */ +#ifndef libparc_parc_SafeMemory_h +#define libparc_parc_SafeMemory_h + +#include <stdlib.h> +#include <stdint.h> +#include <stdbool.h> + +#include <parc/algol/parc_Memory.h> + +typedef enum parc_safety_memory_state { + PARCSafeMemoryState_OK = 0, + PARCSafeMemoryState_MISMATCHED = 1, + PARCSafeMemoryState_UNDERRUN = 2, + PARCSafeMemoryState_OVERRUN = 3, + PARCSafeMemoryState_NOTHINGALLOCATED = 4, + PARCSafeMemoryState_ALREADYFREE = 5 +} PARCSafeMemoryState; + +/** + * Generate a readable, null-terminated C string representation + * for the specified `PARCSafeMemoryState` type. + * + * @param [in] status A `PARCSafeMemoryState` value. + * + * @return A null-terminated C string that must be freed when done. + * + * Example: + * @code + * { + * size_t size = 100; + * uint32_t alignment = sizeof(void *); + * void *memory; + * + * memory = parcSafeMemory_Allocate(size); + * PARCSafeMemoryState state = parcSafeMemory_Destroy(&memory); + * + * printf("SafeMemoryState = %s\n", parcSafeMemoryState_ToString(state)); + * } + * @endcode + */ +const char *parcSafeMemoryState_ToString(PARCSafeMemoryState status); + +/** + * Memory operations defined by {@link PARCMemoryInterface} + * and implemented by the Safe Memory functions. + */ +extern PARCMemoryInterface PARCSafeMemoryAsPARCMemory; + +/** + * Allocate Safe Memory. + * + * Allocate memory through the configured memory allocator, setting the environment to track this memory. + * + * @param [in] size The number of bytes necessary. + * + * @return non-NULL A pointer to allocated memory. + * @return NULL Memory could not be allocated. + * + * Example: + * @code + * { + * size_t size = 100; + * void *memory = parcSafeMemory_Allocate(size); + * } + * @endcode + */ +void *parcSafeMemory_Allocate(size_t size); + +/** + * Allocate Safe Memory. + * + * Allocate memory through the configured memory allocator, setting the environment to track this memory. + * + * @param [in] requestedSize The number of bytes necessary. + * + * @return non-NULL A pointer to allocated memory. + * @return NULL Memory could not be allocated. + * + * Example: + * @code + * { + * size_t size = 100; + * void *memory = parcSafeMemory_Allocate(size); + * } + * @endcode + */ +void *parcSafeMemory_AllocateAndClear(size_t requestedSize); + +/** + * Allocate aligned memory. + * + * Allocates @p size bytes of memory such that the allocation's + * base address is an exact multiple of alignment, + * and returns the allocation in the value pointed to by @p pointer. + * + * The requested alignment must be a power of 2 greater than or equal to `sizeof(void *)`. + * + * Memory that is allocated can be used as an argument in subsequent call `Reallocate`, however + * `Reallocate` is not guaranteed to preserve the original alignment. + * + * @param [out] pointer A pointer to a `void *` pointer that will be set to the address of the allocated memory. + * @param [in] alignment A power of 2 greater than or equal to `sizeof(void *)` + * @param [in] size The number of bytes to allocate. + * + * @return 0 Successful + * @return EINVAL The alignment parameter is not a power of 2 at least as large as sizeof(void *) + * @return ENOMEM Memory allocation error. + * + * Example: + * @code + * { + * void *allocatedMemory; + * + * int failure = parcSafeMemory_MemAlign(&allocatedMemory, sizeof(void *), 100); + * if (failure == 0) { + * parcSafeMemory_Deallocate(&allocatedMemory); + * // allocatedMemory is now equal to zero. + * } + * } + * @endcode + * @see `posix_memalign` + */ +int parcSafeMemory_MemAlign(void **pointer, size_t alignment, size_t size); + +/** + * Deallocate memory previously allocated with {@link parcSafeMemory_Allocate} + * + * The value pointed to by @p pointer will be set to NULL. + * + * @param [in,out] pointer A pointer to a pointer to the allocated memory. + * + * Example: + * @code + * { + * size_t size = 100; + * void *memory = parcSafeMemory_Allocate(size); + * + * parcSafeMemory_Deallocate(&memory); + * } + * @endcode + */ +void parcSafeMemory_Deallocate(void **pointer); + +/** + * A (mostly) suitable replacement for realloc(3). + * The primary difference is that it is an error if newSize is zero. + * If the newSize is equal to the old size, then NULL is returned. + * + * @param [in] original Pointer to the original memory + * @param [in] newSize The size of the newly re-allocated memory. + * + * @return Non-NULL A pointer to the newly allocated memory + * @return NULL An error occurred (newSize == oldSize, or newSize == 0) + * + * Example: + * @code + * { + * void *memory = parcSafeMemory_Allocate(100); + * + * size_t newLength = 0; + * unsigned char *newMemory = parcSafeMemory_Reallocate(memory, newLength); + * + * assertTrue(newMemory == NULL, "Expected NULL, actual %p", newMemory); + * } + * @endcode + */ +void *parcSafeMemory_Reallocate(void *original, size_t newSize); + +/** + * Duplicate the given null-terminated C string. + * + * @param [in] string A pointer to a null-terminated C string. + * @param [in] length The length of the string, not including the terminating null character. + * + * @return non-NULL Allocated Safe Memory containing the duplicate string. This must be freed via `parcSafeMemory_Deallocate`. + * @return NULL Memory could not be allocated. + * + * Example: + * @code + * { + * char *string = "hello world"; + * char *actual = parcSafeMemory_StringDuplicate(string, strlen(string)); + * ... + * } + * @endcode + * + * @see parcSafeMemory_Deallocate + */ +char *parcSafeMemory_StringDuplicate(const char *string, size_t length); + +/** + * Return the number of outstanding allocations. + * + * In practice, every allocation should be matched with a corresponding deallocation. + * This return the number of allocations that have not been deallocated. + * + * @return The number of outstanding allocations. + * + */ +uint32_t parcSafeMemory_Outstanding(void); + +/** + * Display information about outstanding memory allocations. + * + * To enable this function, you must include the following line in your execution before any allocations are performed. + * + * @code + * { + * parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + * } + * @endcode + * + * @param [in] outputFd Output file descriptor. + * + * @return The number of currenly outstanding allocations. + * + * Example: + * @code + * { + * parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + * + * ... + * + * FILE *fd = fopen ("log.txt", "w"); + * size_t outstandingAllocations = parcSafeMemory_ReportAllocation(fd); + * } + * @endcode + */ +uint32_t parcSafeMemory_ReportAllocation(int outputFd); + +/** + * Determine if a pointer to Safe Memory is valid. + * + * Invalid indicates the memory is overrun or underrun. + * + * To enable this function, you must include the following line in your execution before any allocations are performed. + * + * @code + * { + * parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + * } + * @endcode + * + * @param [in] memory A pointer to previously allocated Safe Memory. + * + * @return true The memory is valid; + * @return false The memory is invalid; + * + * Example: + * @code + * { + * void *memory = parcSafeMemory_Allocate(100); + * if (parcSafeMemory_IsValid(memory) == false) { + * printf("Memory is invalid\n"); + * } + * @endcode + */ +bool parcSafeMemory_IsValid(const void *memory); + +/** + * Print a human readable representation of the given PARC Safe Memory array. + * + * To enable this function, you must include the following line in your execution before any allocations are performed. + * + * @code + * { + * parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + * } + * @endcode + * + * @param [in] indentation The level of indentation to use to pretty-print the output. + * @param [in] memory A pointer to the memory to display. + * + * Example: + * @code + * { + * PARCBuffer *instance = parcBuffer_Create(); + * + * parcBuffer_Display(instance, 0); + * + * parcBuffer_Release(&instance); + * } + * @endcode + */ +void parcSafeMemory_Display(const void *memory, int indentation); +#endif // libparc_parc_SafeMemory_h diff --git a/libparc/parc/algol/parc_SortedList.c b/libparc/parc/algol/parc_SortedList.c new file mode 100644 index 00000000..8a941701 --- /dev/null +++ b/libparc/parc/algol/parc_SortedList.c @@ -0,0 +1,252 @@ +/* + * 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 <parc/algol/parc_Object.h> +#include <parc/algol/parc_DisplayIndented.h> +#include <parc/algol/parc_Memory.h> + +#include <parc/algol/parc_SortedList.h> +#include <parc/algol/parc_LinkedList.h> + +struct PARCSortedList { + PARCLinkedList *list; + PARCSortedListEntryCompareFunction compare; +}; + +static void +_parcSortedList_Finalize(PARCSortedList **instancePtr) +{ + assertNotNull(instancePtr, "Parameter must be a non-null pointer to a PARCSortedList pointer."); + PARCSortedList *instance = *instancePtr; + + parcSortedList_OptionalAssertValid(instance); + + parcLinkedList_Release(&instance->list); +} + +parcObject_ImplementAcquire(parcSortedList, PARCSortedList); + +parcObject_ImplementRelease(parcSortedList, PARCSortedList); + +parcObject_ExtendPARCObject(PARCSortedList, _parcSortedList_Finalize, parcSortedList_Copy, parcSortedList_ToString, parcSortedList_Equals, NULL, parcSortedList_HashCode, parcSortedList_ToJSON); + + +void +parcSortedList_AssertValid(const PARCSortedList *instance) +{ + assertTrue(parcSortedList_IsValid(instance), + "PARCSortedList is not valid."); +} + + +PARCSortedList * +parcSortedList_Create(void) +{ + PARCSortedList *result = parcSortedList_CreateCompare(parcObject_Compare); + + return result; +} + +PARCSortedList * +parcSortedList_CreateCompare(PARCSortedListEntryCompareFunction compare) +{ + PARCSortedList *result = parcObject_CreateInstance(PARCSortedList); + + if (result != NULL) { + result->list = parcLinkedList_Create(); + result->compare = compare; + } + + return result; +} + +PARCSortedList * +parcSortedList_Copy(const PARCSortedList *original) +{ + PARCSortedList *result = parcObject_CreateInstance(PARCSortedList); + + if (result != NULL) { + result->list = parcLinkedList_Copy(original->list); + } + + return result; +} + +void +parcSortedList_Display(const PARCSortedList *instance, int indentation) +{ + parcDisplayIndented_PrintLine(indentation, "PARCSortedList@%p {", instance); + parcLinkedList_Display(instance->list, indentation + 1); + parcDisplayIndented_PrintLine(indentation, "}"); +} + +bool +parcSortedList_Equals(const PARCSortedList *x, const PARCSortedList *y) +{ + return parcLinkedList_Equals(x->list, y->list); +} + +PARCHashCode +parcSortedList_HashCode(const PARCSortedList *instance) +{ + PARCHashCode result = parcLinkedList_HashCode(instance->list); + + return result; +} + +bool +parcSortedList_IsValid(const PARCSortedList *instance) +{ + bool result = false; + + if (instance != NULL) { + result = true; + } + + return result; +} + +PARCJSON * +parcSortedList_ToJSON(const PARCSortedList *instance) +{ + PARCJSON *result = parcJSON_Create(); + + return result; +} + +char * +parcSortedList_ToString(const PARCSortedList *instance) +{ + char *result = parcMemory_Format("PARCSortedList@%p\n", instance); + + return result; +} + +size_t +parcSortedList_Size(const PARCSortedList *list) +{ + return parcLinkedList_Size(list->list); +} + +PARCObject * +parcSortedList_GetAtIndex(const PARCSortedList *list, const size_t index) +{ + return parcLinkedList_GetAtIndex(list->list, index); +} + +PARCObject * +parcSortedList_GetFirst(const PARCSortedList *list) +{ + return parcLinkedList_GetAtIndex(list->list, 0); +} + +PARCObject * +parcSortedList_GetLast(const PARCSortedList *list) +{ + return parcLinkedList_GetAtIndex(list->list, parcLinkedList_Size(list->list) - 1); +} + +PARCObject * +parcSortedList_RemoveFirst(PARCSortedList *list) +{ + PARCObject *result = parcLinkedList_RemoveFirst(list->list); + + return result; +} + +PARCObject * +parcSortedList_RemoveLast(PARCSortedList *list) +{ + PARCObject *result = parcLinkedList_RemoveLast(list->list); + + return result; +} + +bool +parcSortedList_Remove(PARCSortedList *list, const PARCObject *object) +{ + bool result = false; + + PARCIterator *iterator = parcSortedList_CreateIterator(list); + + while (parcIterator_HasNext(iterator)) { + PARCObject *o = parcIterator_Next(iterator); + if (parcObject_Equals(object, o)) { + parcIterator_Remove(iterator); + result = true; + break; + } + } + + parcIterator_Release(&iterator); + + return result; +} + +static size_t +_parcSortedList_GetInsertionIndex(const PARCSortedList *instance, const PARCObject *element) +{ + ssize_t low = 0; + ssize_t high = parcLinkedList_Size(instance->list) - 1; + + if (high == -1) { + return 0; + } + + while (true) { + ssize_t midpoint = (low + (high - low) / 2); + PARCObject *e = parcLinkedList_GetAtIndex(instance->list, midpoint); + int signum = instance->compare(element, e); + if (high == low) { + if (signum < 0) { + return high; + } else if (signum > 0) { + return low + 1; + } else { + return low; + } + } + + if (signum < 0) { + high = midpoint; + } else if (signum > 0) { + low = midpoint + 1; + } else { + return midpoint; + } + } + + return -1; +} + +PARCIterator * +parcSortedList_CreateIterator(PARCSortedList *instance) +{ + return parcLinkedList_CreateIterator(instance->list); +} + +void +parcSortedList_Add(PARCSortedList *instance, PARCObject *element) +{ + size_t insertionPoint = _parcSortedList_GetInsertionIndex(instance, element); + assertTrue(insertionPoint >= 0 && insertionPoint <= parcLinkedList_Size(instance->list), + "%zd is bad insertion point. Must be >=0 and <= %zd", insertionPoint, parcLinkedList_Size(instance->list)); + + parcLinkedList_InsertAtIndex(instance->list, insertionPoint, element); +} diff --git a/libparc/parc/algol/parc_SortedList.h b/libparc/parc/algol/parc_SortedList.h new file mode 100644 index 00000000..393992e7 --- /dev/null +++ b/libparc/parc/algol/parc_SortedList.h @@ -0,0 +1,595 @@ +/* + * 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. + */ + +/** + * @file parc_SortedList.h + * @ingroup datastructures + * @brief <#Brief Description#> + * + * <#Detailed Description#> + * + */ +#ifndef PARCLibrary_parc_SortedList +#define PARCLibrary_parc_SortedList +#include <stdbool.h> + +#include <parc/algol/parc_JSON.h> +#include <parc/algol/parc_HashCode.h> +#include <parc/algol/parc_Iterator.h> + +struct PARCSortedList; +typedef struct PARCSortedList PARCSortedList; + +typedef int (*PARCSortedListEntryCompareFunction)(const PARCObject *objA, const PARCObject *objB); + +/** + * Increase the number of references to a `PARCSortedList` instance. + * + * Note that new `PARCSortedList` is not created, + * only that the given `PARCSortedList` reference count is incremented. + * Discard the reference by invoking `parcSortedList_Release`. + * + * @param [in] instance A pointer to a valid PARCSortedList instance. + * + * @return The same value as @p instance. + * + * Example: + * @code + * { + * PARCSortedList *a = parcSortedList_Create(); + * + * PARCSortedList *b = parcSortedList_Acquire(); + * + * parcSortedList_Release(&a); + * parcSortedList_Release(&b); + * } + * @endcode + */ +PARCSortedList *parcSortedList_Acquire(const PARCSortedList *instance); + +#ifdef PARCLibrary_DISABLE_VALIDATION +# define parcSortedList_OptionalAssertValid(_instance_) +#else +# define parcSortedList_OptionalAssertValid(_instance_) parcSortedList_AssertValid(_instance_) +#endif + +/** + * Assert that the given `PARCSortedList` instance is valid. + * + * @param [in] instance A pointer to a valid PARCSortedList instance. + * + * Example: + * @code + * { + * PARCSortedList *a = parcSortedList_Create(); + * + * parcSortedList_AssertValid(a); + * + * printf("Instance is valid.\n"); + * + * parcSortedList_Release(&b); + * } + * @endcode + */ +void parcSortedList_AssertValid(const PARCSortedList *instance); + +/** + * Create an instance of PARCSortedList + * + * <#Paragraphs Of Explanation#> + * + * @return non-NULL A pointer to a valid PARCSortedList instance. + * @return NULL An error occurred. + * + * Example: + * @code + * { + * PARCSortedList *a = parcSortedList_Create(); + * + * parcSortedList_Release(&a); + * } + * @endcode + */ +PARCSortedList *parcSortedList_Create(void); + +/** + * Create an instance of PARCSortedList and provide a comparison function. + * + * @param [in] compare A pointer to a function that implements the Compare contract. + * + * @return non-NULL A pointer to a valid PARCSortedList instance. + * @return NULL An error occurred. + * + * Example: + * @code + * { + * PARCSortedList *a = parcSortedList_Create(); + * + * parcSortedList_Release(&a); + * } + * @endcode + */ +PARCSortedList *parcSortedList_CreateCompare(PARCSortedListEntryCompareFunction compare); + +/** + * Create an independent copy the given `PARCBuffer` + * + * A new buffer is created as a complete copy of the original. + * + * @param [in] original A pointer to a valid PARCSortedList instance. + * + * @return NULL Memory could not be allocated. + * @return non-NULL A pointer to a new `PARCSortedList` instance. + * + * Example: + * @code + * { + * PARCSortedList *a = parcSortedList_Create(); + * + * PARCSortedList *copy = parcSortedList_Copy(&b); + * + * parcSortedList_Release(&b); + * parcSortedList_Release(©); + * } + * @endcode + */ +PARCSortedList *parcSortedList_Copy(const PARCSortedList *original); + +/** + * Print a human readable representation of the given `PARCSortedList`. + * + * @param [in] instance A pointer to a valid PARCSortedList instance. + * @param [in] indentation The indentation level to use for printing. + * + * Example: + * @code + * { + * PARCSortedList *a = parcSortedList_Create(); + * + * parcSortedList_Display(a, 0); + * + * parcSortedList_Release(&a); + * } + * @endcode + */ +void parcSortedList_Display(const PARCSortedList *instance, int indentation); + +/** + * Determine if two `PARCSortedList` instances are equal. + * + * The following equivalence relations on non-null `PARCSortedList` instances are maintained: * + * * It is reflexive: for any non-null reference value x, `parcSortedList_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `parcSortedList_Equals(x, y)` must return true if and only if + * `parcSortedList_Equals(y x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcSortedList_Equals(x, y)` returns true and + * `parcSortedList_Equals(y, z)` returns true, + * then `parcSortedList_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `parcSortedList_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `parcSortedList_Equals(x, NULL)` must return false. + * + * @param [in] x A pointer to a valid PARCSortedList instance. + * @param [in] y A pointer to a valid PARCSortedList instance. + * + * @return true The instances x and y are equal. + * + * Example: + * @code + * { + * PARCSortedList *a = parcSortedList_Create(); + * PARCSortedList *b = parcSortedList_Create(); + * + * if (parcSortedList_Equals(a, b)) { + * printf("Instances are equal.\n"); + * } + * + * parcSortedList_Release(&a); + * parcSortedList_Release(&b); + * } + * @endcode + * @see parcSortedList_HashCode + */ +bool parcSortedList_Equals(const PARCSortedList *x, const PARCSortedList *y); + +/** + * Returns a hash code value for the given instance. + * + * The general contract of `HashCode` is: + * + * Whenever it is invoked on the same instance more than once during an execution of an application, + * the `HashCode` function must consistently return the same value, + * provided no information used in a corresponding comparisons on the instance is modified. + * + * This value need not remain consistent from one execution of an application to another execution of the same application. + * If two instances are equal according to the {@link parcSortedList_Equals} method, + * then calling the {@link parcSortedList_HashCode} method on each of the two instances must produce the same integer result. + * + * It is not required that if two instances are unequal according to the + * {@link parcSortedList_Equals} function, + * then calling the `parcSortedList_HashCode` + * method on each of the two objects must produce distinct integer results. + * + * @param [in] instance A pointer to a valid PARCSortedList instance. + * + * @return The hashcode for the given instance. + * + * Example: + * @code + * { + * PARCSortedList *a = parcSortedList_Create(); + * + * PARCHashCode hashValue = parcSortedList_HashCode(buffer); + * parcSortedList_Release(&a); + * } + * @endcode + */ +PARCHashCode parcSortedList_HashCode(const PARCSortedList *instance); + +/** + * Determine if an instance of `PARCSortedList` is valid. + * + * Valid means the internal state of the type is consistent with its required current or future behaviour. + * This may include the validation of internal instances of types. + * + * @param [in] instance A pointer to a valid PARCSortedList instance. + * + * @return true The instance is valid. + * @return false The instance is not valid. + * + * Example: + * @code + * { + * PARCSortedList *a = parcSortedList_Create(); + * + * if (parcSortedList_IsValid(a)) { + * printf("Instance is valid.\n"); + * } + * + * parcSortedList_Release(&a); + * } + * @endcode + * + */ +bool parcSortedList_IsValid(const PARCSortedList *instance); + +/** + * Release a previously acquired reference to the given `PARCSortedList` instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] instancePtr A pointer to a pointer to the instance to release. + * + * Example: + * @code + * { + * PARCSortedList *a = parcSortedList_Create(); + * + * parcSortedList_Release(&a); + * } + * @endcode + */ +void parcSortedList_Release(PARCSortedList **instancePtr); + +/** + * Create a `PARCJSON` instance (representation) of the given object. + * + * @param [in] instance A pointer to a valid PARCSortedList instance. + * + * @return NULL Memory could not be allocated to contain the `PARCJSON` instance. + * @return non-NULL An allocated C string that must be deallocated via parcMemory_Deallocate(). + * + * Example: + * @code + * { + * PARCSortedList *a = parcSortedList_Create(); + * + * PARCJSON *json = parcSortedList_ToJSON(a); + * + * printf("JSON representation: %s\n", parcJSON_ToString(json)); + * parcJSON_Release(&json); + * + * parcSortedList_Release(&a); + * } + * @endcode + */ +PARCJSON *parcSortedList_ToJSON(const PARCSortedList *instance); + +/** + * Produce a null-terminated string representation of the specified `PARCSortedList`. + * + * The result must be freed by the caller via {@link parcMemory_Deallocate}. + * + * @param [in] instance A pointer to a valid PARCSortedList instance. + * + * @return NULL Cannot allocate memory. + * @return non-NULL A pointer to an allocated, null-terminated C string that must be deallocated via {@link parcMemory_Deallocate}. + * + * Example: + * @code + * { + * PARCSortedList *a = parcSortedList_Create(); + * + * char *string = parcSortedList_ToString(a); + * + * parcSortedList_Release(&a); + * + * parcMemory_Deallocate(&string); + * } + * @endcode + * + * @see parcSortedList_Display + */ +char *parcSortedList_ToString(const PARCSortedList *instance); + +/** + * Wakes up a single thread that is waiting on this object (see `parcSortedList_Wait)`. + * If any threads are waiting on this object, one of them is chosen to be awakened. + * The choice is arbitrary and occurs at the discretion of the underlying implementation. + * + * The awakened thread will not be able to proceed until the current thread relinquishes the lock on this object. + * The awakened thread will compete in the usual manner with any other threads that might be actively + * competing to synchronize on this object; + * for example, the awakened thread enjoys no reliable privilege or disadvantage in being the next thread to lock this object. + * + * @param [in] object A pointer to a valid PARCSortedList instance. + * + * Example: + * @code + * { + * + * parcSortedList_Notify(object); + * } + * @endcode + */ +parcObject_ImplementNotify(parcSortedList, PARCSortedList); + +/** + * Wakes up all threads that are waiting on the given object's lock. + * + * A thread waits on an object by calling one of the wait methods, `parcSortedList_Wait`, `parcSortedList_WaitFor`, `parcSortedList_WaitUntil`. + * The awakened threads will proceed after the current thread relinquishes the lock on the given object. + * The awakened threads will compete in the usual manner with any other threads that might be actively competing + * to synchronize on this object. + * Awakened threads have no priority between them in being the next thread to lock this object. + * + * This method can only be called by a thread that is the owner of this object's lock. + * + * @param [in] object A pointer to a valid `PARCSortedList` instance. + * + * Example: + * @code + * { + * if (parcSortedList_Lock(object)) { + * parcSortedList_NotifyAll(object); + * parcSortedList_Unlock(object); + * } + * } + * @endcode + */ +parcObject_ImplementNotifyAll(parcSortedList, PARCSortedList); + +/** + * Causes the calling thread to wait until either another thread invokes the parcSortedList_Notify() function on the same object. + * * + * @param [in] object A pointer to a valid `PARCSortedList` instance. + * + * Example: + * @code + * { + * + * parcSortedList_Wait(object); + * } + * @endcode + */ +parcObject_ImplementWait(parcSortedList, PARCSortedList); + +parcObject_ImplementWaitFor(parcSortedList, PARCSortedList); + +/** + * Causes the calling thread to wait until either another thread invokes the `parcSortedList_Notify()` + * function on the same object or the system time equals or exceeds the specified time. + * + * The calling thread must own the object's lock. + * The calling thread will release ownership of this lock and wait until another thread invokes + * `parcSortedList_Notify` or the computer's system time equals or exceeds that specified by @p time. + * on the same object. + * The original calling thread then re-obtains ownership of the lock and resumes execution. + * + * This function must only be called by a thread that is the owner of this object's lock. + * + * @param [in] object A pointer to a valid PARCSortedList instance. + * + * @returns false if the alloted time was exceeded. + * @returns true if another thread invoked the `parcSortedList_Notify()` or `parcSortedList_NotifyAll()` function + * + * Example: + * @code + * { + * struct timeval tv; + * gettimeofday(&tv, NULL); + * + * struct timespec absoluteTime; + * absoluteTime.tv_sec = tv.tv_sec + 0; + * absoluteTime.tv_nsec = 0; + * + * parcSortedList_WaitUntil(object, &absoluteTime); + * } + * @endcode + */ +parcObject_ImplementWaitUntil(parcSortedList, PARCSortedList); + +/** + * Obtain the lock on the given `PARCSortedList` instance. + * + * If the lock is already held by another thread, this function will block. + * If the lock is aleady held by the current thread, this function will return `false`. + * + * Implementors must avoid deadlock by attempting to lock the object a second time within the same calling thread. + * + * @param [in] object A pointer to a valid `PARCSortedList` instance. + * + * @return true The lock was obtained successfully. + * @return false The lock is already held by the current thread, or the `PARCSortedList` is invalid. + * + * Example: + * @code + * { + * if (parcSortedList_Lock(object)) { + * + * } + * } + * @endcode + */ +parcObject_ImplementLock(parcSortedList, PARCSortedList); + +/** + * Try to obtain the advisory lock on the given PARCSortedList instance. + * + * Once the lock is obtained, the caller must release the lock as soon as possible. + * + * @param [in] object A pointer to a valid PARCSortedList instance. + * + * @return true The PARCSortedList is locked. + * @return false The PARCSortedList is unlocked. + * + * Example: + * @code + * { + * parcSortedList_TryLock(object); + * } + * @endcode + */ +parcObject_ImplementTryLock(parcSortedList, PARCSortedList); + +/** + * Try to unlock the advisory lock on the given `PARCSortedList` instance. + * + * @param [in] object A pointer to a valid `PARCSortedList` instance. + * + * @return true The `PARCSortedList` was locked and now is unlocked. + * @return false The `PARCSortedList` was not locked and remains unlocked. + * + * Example: + * @code + * { + * parcSortedList_Unlock(object); + * } + * @endcode + */ +parcObject_ImplementUnlock(parcSortedList, PARCSortedList); + +/** + * Determine if the advisory lock on the given `PARCSortedList` instance is locked. + * + * @param [in] object A pointer to a valid `PARCSortedList` instance. + * + * @return true The `PARCSortedList` is locked. + * @return false The `PARCSortedList` is unlocked. + * Example: + * @code + * { + * if (parcSortedList_IsLocked(object)) { + * ... + * } + * } + * @endcode + */ +parcObject_ImplementIsLocked(parcSortedList, PARCSortedList); + +PARCIterator *parcSortedList_CreateIterator(PARCSortedList *instance); + +void parcSortedList_Add(PARCSortedList *instance, PARCObject *element); + +size_t parcSortedList_Size(const PARCSortedList *list); + +PARCObject *parcSortedList_GetAtIndex(const PARCSortedList *list, const size_t index); + +/** + * Return the first element of the specified list. + * The element's reference count is not modified, + * the caller must acquire its own reference to the element if it is needed beyond lifetime of the given list. + * + * @param [in] list A pointer to the instance of `PARCSortedList` from which the first element will be returned. + * + * @return non NULL A pointer to the element + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCObject *parcSortedList_GetFirst(const PARCSortedList *list); + +/** + * Return the last element of the specified list. + * The element's reference count is not modified, + * the caller must acquire its own reference to the element if it is needed beyond lifetime of the given list. + * + * @param [in] list A pointer to the instance of `PARCSortedList` from which the last element will be returned. + * + * @return non NULL A pointer to the element + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCObject *parcSortedList_GetLast(const PARCSortedList *list); + + +bool parcSortedList_Remove(PARCSortedList *list, const PARCObject *object); +/** + * Return the first element of the specified list and remove it from the list. + * The element's reference count is not modified, + * the caller must release the returned element when it is finished with it. + * + * @param [in] list A pointer to the instance of `PARCSortedList` from which the first element will be returned and removed + * + * @return non NULL A pointer to the element removed + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCObject *parcSortedList_RemoveFirst(PARCSortedList *list); + +/** + * Return the last element of the specified list and remove it from the list. + * The element's reference count is not modified, + * the caller must release the returned element when it is finished with it. + * + * @param [in] list A pointer to the instance of `PARCSortedList` from which the last element will be removed and returned. + * + * @return non-NULL A pointer to the element removed + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCObject *parcSortedList_RemoveLast(PARCSortedList *list); + +#endif diff --git a/libparc/parc/algol/parc_Stack.c b/libparc/parc/algol/parc_Stack.c new file mode 100755 index 00000000..5930169b --- /dev/null +++ b/libparc/parc/algol/parc_Stack.c @@ -0,0 +1,76 @@ +/* + * 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 <stdio.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Deque.h> +#include <parc/algol/parc_Stack.h> + +struct parc_stack { + void *instance; + PARCStackInterface *interface; +}; + +PARCStack * +parcStack(void *instance, PARCStackInterface *interface) +{ + PARCStack *result = parcMemory_AllocateAndClear(sizeof(PARCStack)); + assertNotNull(result, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(PARCStack)); + result->instance = instance; + result->interface = interface; + + return result; +} + +void +parcStack_Release(PARCStack **stackPtr) +{ + PARCStack *stack = *stackPtr; + (stack->interface->parcStack_Release)(&stack->instance); + parcMemory_Deallocate((void **) &stack); + + *stackPtr = 0; +} + +bool +parcStack_IsEmpty(const PARCStack *stack) +{ + return (stack->interface->parcStack_IsEmpty)(stack->instance); +} + +void * +parcStack_Peek(const PARCStack *stack) +{ + return (stack->interface->parcStack_Peek)(stack->instance); +} + +void * +parcStack_Pop(PARCStack *stack) +{ + return (stack->interface->parcStack_Pop)(stack->instance); +} + +void * +parcStack_Push(PARCStack *stack, void *element) +{ + return (stack->interface->parcStack_Push)(stack->instance, element); +} diff --git a/libparc/parc/algol/parc_Stack.h b/libparc/parc/algol/parc_Stack.h new file mode 100755 index 00000000..e64e02f4 --- /dev/null +++ b/libparc/parc/algol/parc_Stack.h @@ -0,0 +1,140 @@ +/* + * 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. + */ + +/** + * @file parc_Stack.h + * @ingroup datastructures + * @brief PARC (Generic) Stack + * + * A stack interface implementation. + * + * Example + * @code + * #include <parc/algol/parc_Deque.h> + * + * int + * main(int argc, char *argv[]) + * { + * PARCStackInterface dequeAsStack = { + * .parcStack_Release = parcDeque_Release, + * .parcStack_IsEmpty = parcDeque_IsEmpty, + * .parcStack_Peek = parcDeque_PeekLast, + * .parcStack_Pop = parcDeque_RemoveLast, + * .parcStack_Push = parcDeque_Append, + * .parcStack_Search = NULL + * }; + * + * PARCStack *stack = parcStack(parcDeque_Create(), &dequeAsStack); + * + * parcStack_IsEmpty(stack); + * } + * @endcode + * + * + */ +#ifndef libparc_parc_Stack_h +#define libparc_parc_Stack_h + +#include <stdbool.h> + +struct parc_stack; +typedef struct parc_stack PARCStack; + +typedef struct parc_stack_interface { + /** + * Release the instance + * + * + * @param [in,out] instancePtr A pointer to the pointer of the instance to release. + * + * Example: + * @code + * <#example#> + * @endcode + */ + void (*parcStack_Release)(void **instancePtr); + + /** + * Tests if this stack is empty. + * + * @param [in] instance A pointer to the instance to test. + * @return true if the stack is empty + * @return false if the stack is not empty + * + * Example: + * @code + * <#example#> + * @endcode + */ + bool (*parcStack_IsEmpty)(const void *instance); + + /** + * Looks at the object at the top of this stack without removing it from the stack. + * + * @param [in] instance A pointer to the instance to look at. + * @return The object at the top of the @p instance. + * + * Example: + * @code + * <#example#> + * @endcode + */ + void * (*parcStack_Peek)(const void *instance); + + /** + * Removes the object at the top of this stack and returns that object as the value of this function. + * + * + * @param [in,out] instance A pointer to the instance to check and modify. + * @return The object at the top of the @p instance. + * + * Example: + * @code + * <#example#> + * @endcode + */ + void * (*parcStack_Pop)(void *instance); + + /** + * Pushes an item onto the top of this stack. + * + * + * @param [in,out] instance A pointer to the instance to modify. + * @param [in] item A pointer to the object to push on the @p instance. + * @return A pointer to the object that was pushed on the @p instance. + * + * Example: + * @code + * <#example#> + * @endcode + */ + void * (*parcStack_Push)(void *instance, void *item); + + /** + * Returns the 1-based position where an object is on this stack. + * + * + * @param [in] instance A pointer to the instance. + * @param [in] element A pointer to the element to find on the @p instance. + * @return The index of the position where @p element is found in the @p instance. + * + * Example: + * @code + * <#example#> + * @endcode + */ + int (*parcStack_Search)(void *instance, void *element); +} PARCStackInterface; +#endif // libparc_parc_Stack_h diff --git a/libparc/parc/algol/parc_StandardOutputStream.c b/libparc/parc/algol/parc_StandardOutputStream.c new file mode 100644 index 00000000..0c46ce3c --- /dev/null +++ b/libparc/parc/algol/parc_StandardOutputStream.c @@ -0,0 +1,76 @@ +/* + * 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 <parc/algol/parc_StandardOutputStream.h> +#include <parc/algol/parc_OutputStream.h> +#include <parc/algol/parc_Object.h> + +struct PARCStandardOutputStream { + int fd; +}; + +static struct PARCOutputStreamImplementation _implementation = { + .Write = PARCStandardOutputStream_Write, + .Acquire = PARCStandardOutputStream_Acquire, + .Release = PARCStandardOutputStream_Release; +}; + +static void +_parcStandardOutputStream_Destroy(PARCStandardOutputStream **streamPtr) +{ + parcObject_Release((void **) streamPtr); +} + +static const PARCObjectImpl _parcStandardOutputStream_Object = { + .destroy = (PARCObjectDestroy *) _parcStandardOutputStream_Destroy, + .copy = NULL, + .toString = NULL, + .equals = NULL, + .compare = NULL +}; + +PARCOutputStream * +parcStandardOutputStream(void) +{ + PARCStandardOutputStream *instance = PARCStandardOutputStream_Create(); + parcOutputStream(instance, &_implementation) +} + +PARCStandardOutputStream * +PARCStandardOutputStream_Create(void) +{ + parcObject_Create(sizeof(PARCStandardOutputStream), &_parcStandardOutputStream_Object); + return NULL; +} + +PARCStandardOutputStream * +PARCStandardOutputStream_Acquire(PARCStandardOutputStream *instance) +{ + return parcObject_Acquire(instance); +} + +void +PARCStandardOutputStream_Release(PARCStandardOutputStream **instanceP) +{ + parcObject_Release((void **) instanceP); +} + +bool +parcFileOutputStream_Write(PARCStandardOutputStream *stream, PARCBuffer *buffer) +{ + return false; +} diff --git a/libparc/parc/algol/parc_StandardOutputStream.h b/libparc/parc/algol/parc_StandardOutputStream.h new file mode 100644 index 00000000..be5a21d3 --- /dev/null +++ b/libparc/parc/algol/parc_StandardOutputStream.h @@ -0,0 +1,38 @@ +/* + * 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. + */ + +/** + * @file parc_StandardOutputStream.h + * @brief Standard output stream structures and functions. + * + */ + +#ifndef __PARC_Library__parc_StandardOutputStream__ +#define __PARC_Library__parc_StandardOutputStream__ + +struct PARCStandardOutputStream; +typedef struct PARCStandardOutputStream PARCStandardOutputStream; + +#include <parc/algol/parc_OutputStream.h> + +PARCOutputStream *parcStandardOutputStream(void); + +PARCStandardOutputStream *PARCStandardOutputStream_Create(void); + +PARCStandardOutputStream *PARCStandardOutputStream_Acquire(PARCStandardOutputStream *instance); + +void PARCStandardOutputStream_Release(PARCStandardOutputStream **instanceP); + +#endif /* defined(__PARC_Library__parc_StandardOutputStream__) */ diff --git a/libparc/parc/algol/parc_StdlibMemory.c b/libparc/parc/algol/parc_StdlibMemory.c new file mode 100755 index 00000000..6bbbc0b3 --- /dev/null +++ b/libparc/parc/algol/parc_StdlibMemory.c @@ -0,0 +1,180 @@ +/* + * 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 <stdio.h> +#include <stdlib.h> +#include <sys/errno.h> +#include <stdbool.h> +#include <string.h> +#include <strings.h> +#include <pthread.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_StdlibMemory.h> + +static uint32_t _parcStdlibMemory_OutstandingAllocations; + +#ifdef PARCLibrary_DISABLE_ATOMICS +static pthread_mutex_t _parcStdlibMemory_Mutex = PTHREAD_MUTEX_INITIALIZER; + +static inline void +_parcStdlibMemory_IncrementOutstandingAllocations(void) +{ + pthread_mutex_lock(&_parcStdlibMemory_Mutex); + _parcStdlibMemory_OutstandingAllocations++; + pthread_mutex_unlock(&_parcStdlibMemory_Mutex); +} + +static inline void +_parcStdlibMemory_DecrementOutstandingAllocations(void) +{ + pthread_mutex_lock(&_parcStdlibMemory_Mutex); + _parcStdlibMemory_OutstandingAllocations--; + pthread_mutex_unlock(&_parcStdlibMemory_Mutex); +} +#else + +static inline void +_parcStdlibMemory_IncrementOutstandingAllocations(void) +{ + __sync_add_and_fetch(&_parcStdlibMemory_OutstandingAllocations, 1); +} + +static inline void +_parcStdlibMemory_DecrementOutstandingAllocations(void) +{ + __sync_sub_and_fetch(&_parcStdlibMemory_OutstandingAllocations, 1); +} +#endif + +#if HAVE_REALLOC == 0 +static void * +_parcStdlibMemory_rplRealloc(void *oldAlloc, size_t newSize) +{ + if (newSize == 0) { + newSize = 1; + } + + char *newAlloc = malloc(newSize); + + if (oldAlloc != NULL) { + memcpy(newAlloc, oldAlloc, newSize); + free(oldAlloc); + } + return newAlloc; +} +#endif + +void * +parcStdlibMemory_Allocate(size_t size) +{ + if (size == 0) { + return NULL; + } + + void *result = malloc(size); + if (result != NULL) { + _parcStdlibMemory_IncrementOutstandingAllocations(); + } + + return result; +} + +void * +parcStdlibMemory_AllocateAndClear(size_t size) +{ + void *pointer = parcStdlibMemory_Allocate(size); + if (pointer != NULL) { + memset(pointer, 0, size); + } + return pointer; +} + +int +parcStdlibMemory_MemAlign(void **pointer, size_t alignment, size_t size) +{ + if (size == 0) { + return EINVAL; + } + + int failure = posix_memalign(pointer, alignment, size); + + if (failure != 0) { + return failure; + } + if (*pointer == NULL) { + return ENOMEM; + } + + _parcStdlibMemory_IncrementOutstandingAllocations(); + + return 0; +} + +void +parcStdlibMemory_Deallocate(void **pointer) +{ +#ifndef PARCLibrary_DISABLE_VALIDATION + trapIllegalValueIf(_parcStdlibMemory_OutstandingAllocations == 0, + "parcStdlibMemory_Deallocate invoked with nothing left to free (double free somewhere?)\n"); +#endif + free(*pointer); + *pointer = NULL; + + _parcStdlibMemory_DecrementOutstandingAllocations(); +} + +void * +parcStdlibMemory_Reallocate(void *pointer, size_t newSize) +{ +#if HAVE_REALLOC + void *result = realloc(pointer, newSize); +#else + void *result = _parcStdlibMemory_rplRealloc(pointer, newSize); +#endif + + if (pointer == NULL) { + _parcStdlibMemory_IncrementOutstandingAllocations(); + } + return result; +} + +char * +parcStdlibMemory_StringDuplicate(const char *string, size_t length) +{ + _parcStdlibMemory_IncrementOutstandingAllocations(); + return strndup(string, length); +} + +uint32_t +parcStdlibMemory_Outstanding(void) +{ + return _parcStdlibMemory_OutstandingAllocations; +} + +PARCMemoryInterface PARCStdlibMemoryAsPARCMemory = { + .Allocate = (uintptr_t) parcStdlibMemory_Allocate, + .AllocateAndClear = (uintptr_t) parcStdlibMemory_AllocateAndClear, + .MemAlign = (uintptr_t) parcStdlibMemory_MemAlign, + .Deallocate = (uintptr_t) parcStdlibMemory_Deallocate, + .Reallocate = (uintptr_t) parcStdlibMemory_Reallocate, + .StringDuplicate = (uintptr_t) parcStdlibMemory_StringDuplicate, + .Outstanding = (uintptr_t) parcStdlibMemory_Outstanding +}; diff --git a/libparc/parc/algol/parc_StdlibMemory.h b/libparc/parc/algol/parc_StdlibMemory.h new file mode 100755 index 00000000..9abbf915 --- /dev/null +++ b/libparc/parc/algol/parc_StdlibMemory.h @@ -0,0 +1,200 @@ +/* + * 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. + */ + +/** + * @file parc_StdlibMemory.h + * @ingroup datastructures + * + * @brief Standard library memory mangement functions wrapped up to be suitable for use by parc_Memory.[ch] + * + */ +#ifndef libparc_parc_StdlibMemory_h +#define libparc_parc_StdlibMemory_h + +#include <parc/algol/parc_Memory.h> + +extern PARCMemoryInterface PARCStdlibMemoryAsPARCMemory; + +/** + * Allocate memory. + * + * @param [in] size The size of memory to allocate + * + * @return A pointer to the allocated memory. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void *parcStdlibMemory_Allocate(size_t size); + +/** + * Allocate memory of size @p size and clear it. + * + * @param [in] size Size of memory to allocate + * + * @return A pointer to the allocated memory + * + * Example: + * @code + * <#example#> + * @endcode + */ +void *parcStdlibMemory_AllocateAndClear(size_t size); + +/** + * Allocate aligned memory. + * + * Allocates @p size bytes of memory such that the allocation's + * base address is an exact multiple of alignment, + * and returns the allocation in the value pointed to by @p pointer. + * + * The requested alignment must be a power of 2 greater than or equal to `sizeof(void *)`. + * + * Memory that is allocated can be used as an argument in subsequent call `Reallocate`, however + * `Reallocate` is not guaranteed to preserve the original alignment. + * + * @param [out] pointer A pointer to a `void *` pointer that will be set to the address of the allocated memory. + * @param [in] alignment A power of 2 greater than or equal to `sizeof(void *)` + * @param [in] size The number of bytes to allocate. + * + * @return 0 Successful + * @return EINVAL The alignment parameter is not a power of 2 at least as large as sizeof(void *) + * @return ENOMEM Memory allocation error. + * + * Example: + * @code + * { + * void *allocatedMemory; + * + * int failure = parcStdlibMemory_MemAlign(&allocatedMemory, sizeof(void *), 100); + * if (failure == 0) { + * parcStdlibMemory_Deallocate(&allocatedMemory); + * // allocatedMemory is now equal to zero. + * } + * } + * @endcode + * @see {@link parcMemory_MemAlign} + */ +int parcStdlibMemory_MemAlign(void **pointer, size_t alignment, size_t size); + +/** + * Deallocate the memory pointed to by @p pointer + * + * @param [in,out] pointer A pointer to a pointer to the memory to be deallocated + * + * Example: + * @code + * <#example#> + * @endcode + */ +void parcStdlibMemory_Deallocate(void **pointer); + +/** + * Resizes previously allocated memory at @p pointer to @p newSize. If necessary, + * new memory is allocated and the content copied from the old memory to the + * new memory and the old memory is deallocated. + * + * @param [in,out] pointer A pointer to the memory to be reallocated. + * @param [in] newSize The size that the memory to be resized to. + * + * @return A pointer to the memory + * + * Example: + * @code + * <#example#> + * @endcode + */ +void *parcStdlibMemory_Reallocate(void *pointer, size_t newSize); + +/** + * Allocate sufficient memory for a copy of the string @p string, + * copy at most n characters from the string @p string into the allocated memory, + * and return the pointer to allocated memory. + * + * The copied string is always null-terminated. + * + * @param [in] string A pointer to a null-terminated string. + * @param [in] length The maximum allowed length of the resulting copy. + * + * @return non-NULL A pointer to allocated memory. + * @return NULL A an error occurred. + * + * Example: + * @code + * { + * char *string = "this is a string"; + * char *copy = parcStdlibMemory_StringDuplicate(string, strlen(string)); + * + * if (copy != NULL) { + * . . . + * parcStdLibMemory_Deallocate(©); + * } + * } + * @endcode + * + * @see {@link parcStdlibMemory_Deallocate()} + */ +char *parcStdlibMemory_StringDuplicate(const char *string, size_t length); + +/** + * Return the number of outstanding allocations managed by this allocator. + * + * When you allocate memory, this count goes up by one. When you deallocate, it goes down by one. + * A well-behaved program will terminate with a call to `parcStdlibMemory_Outstanding()` returning 0. + * + * @return The number of memory allocations still outstanding (remaining to be deallocated). + * + * Example: + * @code + * { + * uint32_t numberOfAllocations = parcStdlibMemory_Outstanding(); + * } + * @endcode + */ +uint32_t parcStdlibMemory_Outstanding(void); + + +/** + * Replacement function for realloc(3). + * + * The standard `realloc()` function tries to change the size of the allocation pointed to by @p oldAlloc to @p newSize, + * and returns @p oldAlloc. If there is not enough room to enlarge the memory allocation pointed to by @p oldAlloc, + * realloc() creates a new allocation, copies as much of the old data pointed to by @p oldAlloc as will fit to the new allocation, + * frees the old allocation, and returns a pointer to the allocated memory. + * + * If @p oldAlloc is `NULL`, `realloc()` is identical to a call to `malloc()` for size bytes. + * If @p newSize is zero and @p oldAlloc is not NULL, a new, minimum sized object is allocated and the original object is freed. + * When extending a region allocated with `calloc(3)`, `realloc(3)` does not guarantee that the additional memory + * is also zero-filled. + * + * If the realloc(3) function is not compatible with the above constraints + * (i.e., ‘realloc(NULL, 0)’ returns an invalid pointer), then autoconf tools will define + * `realloc` to `rpl_realloc` so that the native realloc is not used in the main project. + * + * @param [in] oldAlloc A previously allocated + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +void *rpl_realloc(void *oldAlloc, size_t newSize); +#endif // libparc_parc_StdlibMemory_h diff --git a/libparc/parc/algol/parc_String.c b/libparc/parc/algol/parc_String.c new file mode 100755 index 00000000..e5af5f71 --- /dev/null +++ b/libparc/parc/algol/parc_String.c @@ -0,0 +1,187 @@ +/* + * 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 <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_DisplayIndented.h> + +#include <parc/algol/parc_String.h> + +struct PARCString { + char *string; +}; + +static bool +_parcString_Destructor(PARCString **instancePtr) +{ + assertNotNull(instancePtr, "Parameter must be a non-null pointer to a PARCString pointer."); + PARCString *string = *instancePtr; + + parcMemory_Deallocate(&string->string); + return true; +} + +parcObject_ImplementAcquire(parcString, PARCString); + +parcObject_ImplementRelease(parcString, PARCString); + +parcObject_Override(PARCString, PARCObject, + .destructor = (PARCObjectDestructor *) _parcString_Destructor, + .copy = (PARCObjectCopy *) parcString_Copy, + .display = (PARCObjectDisplay *) parcString_Display, + .toString = (PARCObjectToString *) parcString_ToString, + .equals = (PARCObjectEquals *) parcString_Equals, + .compare = (PARCObjectCompare *) parcString_Compare, + .hashCode = (PARCObjectHashCode *) parcString_HashCode, + .toJSON = (PARCObjectToJSON *) parcString_ToJSON, + .display = (PARCObjectDisplay *) parcString_Display); + +void +parcString_AssertValid(const PARCString *instance) +{ + assertTrue(parcString_IsValid(instance), + "PARCString is not valid."); +} + +PARCString * +parcString_Create(const char *string) +{ + PARCString *result = parcObject_CreateInstance(PARCString); + if (result != NULL) { + result->string = parcMemory_StringDuplicate(string, strlen(string)); + } + return result; +} + +PARCString * +parcString_CreateFromBuffer(const PARCBuffer *buffer) +{ + PARCString *result = parcString_Create(parcBuffer_Overlay((PARCBuffer *) buffer, 0)); + + return result; +} + +int +parcString_Compare(const PARCString *string, const PARCString *other) +{ + int result = 0; + + if (string == NULL) { + if (other != NULL) { + result = -1; + } + } else if (other == NULL) { + result = 1; + } else { + parcString_OptionalAssertValid(string); + parcString_OptionalAssertValid(other); + + int comparison = strcmp(string->string, other->string); + if (comparison < 0) { + result = -1; + } else if (comparison > 0) { + result = 1; + } + } + + return result; +} + +PARCString * +parcString_Copy(const PARCString *original) +{ + PARCString *result = parcString_Create(original->string); + + return result; +} + +void +parcString_Display(const PARCString *instance, int indentation) +{ + parcDisplayIndented_PrintLine(indentation, "PARCString@%p {", instance); + parcDisplayIndented_PrintLine(indentation + 1, "%s", instance->string); + parcDisplayIndented_PrintLine(indentation, "}"); +} + +bool +parcString_Equals(const PARCString *x, const PARCString *y) +{ + bool result = false; + + if (x == y) { + result = true; + } else if (x == NULL || y == NULL) { + result = false; + } else { + parcString_OptionalAssertValid(x); + parcString_OptionalAssertValid(y); + + result = strcmp(x->string, y->string) == 0; + } + + return result; +} + +PARCHashCode +parcString_HashCode(const PARCString *string) +{ + PARCHashCode result = 0; + + result = parcHashCode_Hash((uint8_t *) string->string, strlen(string->string)); + + return result; +} + +bool +parcString_IsValid(const PARCString *string) +{ + bool result = false; + + if (string != NULL) { + if (string->string != NULL) { + result = true; + } + } + + return result; +} + +PARCJSON * +parcString_ToJSON(const PARCString *string) +{ + PARCJSON *result = parcJSON_Create(); + + return result; +} + +char * +parcString_ToString(const PARCString *string) +{ + char *result = parcMemory_StringDuplicate(string->string, strlen(string->string)); + + return result; +} + +const char * +parcString_GetString(const PARCString *string) +{ + parcString_OptionalAssertValid(string); + + return string->string; +} diff --git a/libparc/parc/algol/parc_String.h b/libparc/parc/algol/parc_String.h new file mode 100644 index 00000000..4fe4307f --- /dev/null +++ b/libparc/parc/algol/parc_String.h @@ -0,0 +1,391 @@ +/* + * 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. + */ + +/** + * @file parc_String.h + * @ingroup types + * @brief <#Brief Description#> + * + * <#Detailed Description#> + * + */ +#ifndef PARCLibrary_parc_String +#define PARCLibrary_parc_String +#include <stdbool.h> +#include <string.h> + +#include <parc/algol/parc_JSON.h> +#include <parc/algol/parc_HashCode.h> + +typedef struct PARCString PARCString; +extern parcObjectDescriptor_Declaration(PARCString); + +/** + * Increase the number of references to a `PARCString` instance. + * + * Note that new `PARCString` is not created, + * only that the given `PARCString` reference count is incremented. + * Discard the reference by invoking `parcString_Release`. + * + * @param [in] instance A pointer to a valid PARCString instance. + * + * @return The same value as @p instance. + * + * Example: + * @code + * { + * PARCString *a = parcString_Create(); + * + * PARCString *b = parcString_Acquire(); + * + * parcString_Release(&a); + * parcString_Release(&b); + * } + * @endcode + */ +PARCString *parcString_Acquire(const PARCString *instance); + +#ifdef PARCLibrary_DISABLE_VALIDATION +# define parcString_OptionalAssertValid(_instance_) +#else +# define parcString_OptionalAssertValid(_instance_) parcString_AssertValid(_instance_) +#endif + +/** + * Assert that the given `PARCString` instance is valid. + * + * @param [in] instance A pointer to a valid PARCString instance. + * + * Example: + * @code + * { + * PARCString *a = parcString_Create(); + * + * parcString_AssertValid(a); + * + * printf("Instance is valid.\n"); + * + * parcString_Release(&b); + * } + * @endcode + */ +void parcString_AssertValid(const PARCString *instance); + +/** + * Create an instance of PARCString + * + * <#Paragraphs Of Explanation#> + * + * @return non-NULL A pointer to a valid PARCString instance. + * @return NULL An error occurred. + * + * Example: + * @code + * { + * PARCString *a = parcString_Create(); + * + * parcString_Release(&a); + * } + * @endcode + */ +PARCString *parcString_Create(const char *); + +/** + * Create an instance of PARCString from the content of a given PARCBuffer. + * + * <#Paragraphs Of Explanation#> + * + * @return non-NULL A pointer to a valid PARCString instance. + * @return NULL An error occurred. + * + * Example: + * @code + * { + * PARCString *a = parcString_Create(); + * + * parcString_Release(&a); + * } + * @endcode + */ +PARCString *parcString_CreateFromBuffer(const PARCBuffer *buffer); + +/** + * Compares @p instance with @p other for order. + * + * Returns a negative integer, zero, or a positive integer as @p instance + * is less than, equal to, or greater than @p other. + * + * @param [in] instance A pointer to a valid PARCString instance. + * @param [in] other A pointer to a valid PARCString instance. + * + * @return <0 Instance is less than @p other. + * @return 0 Instance a and instance b compare the same. + * @return >0 Instance a is greater than instance b. + * + * Example: + * @code + * { + * PARCString *a = parcString_Create(); + * PARCString *b = parcString_Create(); + * + * if (parcString_Compare(a, b) == 0) { + * printf("Instances are equal.\n"); + * } + * + * parcString_Release(&a); + * parcString_Release(&b); + * } + * @endcode + * + * @see parcString_Equals + */ +int parcString_Compare(const PARCString *instance, const PARCString *other); + +/** + * Create an independent copy the given `PARCBuffer` + * + * A new buffer is created as a complete copy of the original. + * + * @param [in] original A pointer to a valid PARCString instance. + * + * @return NULL Memory could not be allocated. + * @return non-NULL A pointer to a new `PARCString` instance. + * + * Example: + * @code + * { + * PARCString *a = parcString_Create(); + * + * PARCString *copy = parcString_Copy(&b); + * + * parcString_Release(&b); + * parcString_Release(©); + * } + * @endcode + */ +PARCString *parcString_Copy(const PARCString *original); + +/** + * Print a human readable representation of the given `PARCString`. + * + * @param [in] instance A pointer to a valid PARCString instance. + * @param [in] indentation The indentation level to use for printing. + * + * Example: + * @code + * { + * PARCString *a = parcString_Create(); + * + * parcString_Display(a, 0); + * + * parcString_Release(&a); + * } + * @endcode + */ +void parcString_Display(const PARCString *instance, int indentation); + +/** + * Determine if two `PARCString` instances are equal. + * + * The following equivalence relations on non-null `PARCString` instances are maintained: * + * * It is reflexive: for any non-null reference value x, `parcString_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `parcString_Equals(x, y)` must return true if and only if + * `parcString_Equals(y x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcString_Equals(x, y)` returns true and + * `parcString_Equals(y, z)` returns true, + * then `parcString_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `parcString_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `parcString_Equals(x, NULL)` must return false. + * + * @param [in] x A pointer to a valid PARCString instance. + * @param [in] y A pointer to a valid PARCString instance. + * + * @return true The instances x and y are equal. + * + * Example: + * @code + * { + * PARCString *a = parcString_Create(); + * PARCString *b = parcString_Create(); + * + * if (parcString_Equals(a, b)) { + * printf("Instances are equal.\n"); + * } + * + * parcString_Release(&a); + * parcString_Release(&b); + * } + * @endcode + * @see parcString_HashCode + */ +bool parcString_Equals(const PARCString *x, const PARCString *y); + +/** + * Returns a hash code value for the given instance. + * + * The general contract of `HashCode` is: + * + * Whenever it is invoked on the same instance more than once during an execution of an application, + * the `HashCode` function must consistently return the same value, + * provided no information used in a corresponding comparisons on the instance is modified. + * + * This value need not remain consistent from one execution of an application to another execution of the same application. + * If two instances are equal according to the {@link parcString_Equals} method, + * then calling the {@link parcString_HashCode} method on each of the two instances must produce the same integer result. + * + * It is not required that if two instances are unequal according to the + * {@link parcString_Equals} function, + * then calling the `parcString_HashCode` + * method on each of the two objects must produce distinct integer results. + * + * @param [in] instance A pointer to a valid PARCString instance. + * + * @return The hashcode for the given instance. + * + * Example: + * @code + * { + * PARCString *a = parcString_Create(); + * + * PARCHashCode hashValue = parcString_HashCode(buffer); + * parcString_Release(&a); + * } + * @endcode + */ +PARCHashCode parcString_HashCode(const PARCString *instance); + +/** + * Determine if an instance of `PARCString` is valid. + * + * Valid means the internal state of the type is consistent with its required current or future behaviour. + * This may include the validation of internal instances of types. + * + * @param [in] instance A pointer to a valid PARCString instance. + * + * @return true The instance is valid. + * @return false The instance is not valid. + * + * Example: + * @code + * { + * PARCString *a = parcString_Create(); + * + * if (parcString_IsValid(a)) { + * printf("Instance is valid.\n"); + * } + * + * parcString_Release(&a); + * } + * @endcode + * + */ +bool parcString_IsValid(const PARCString *instance); + +/** + * Release a previously acquired reference to the given `PARCString` instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] instancePtr A pointer to a pointer to the instance to release. + * + * Example: + * @code + * { + * PARCString *a = parcString_Create(); + * + * parcString_Release(&a); + * } + * @endcode + */ +void parcString_Release(PARCString **instancePtr); + +/** + * Create a `PARCJSON` instance (representation) of the given object. + * + * @param [in] instance A pointer to a valid PARCString instance. + * + * @return NULL Memory could not be allocated to contain the `PARCJSON` instance. + * @return non-NULL An allocated C string that must be deallocated via parcMemory_Deallocate(). + * + * Example: + * @code + * { + * PARCString *a = parcString_Create(); + * + * PARCJSON *json = parcString_ToJSON(a); + * + * printf("JSON representation: %s\n", parcJSON_ToString(json)); + * parcJSON_Release(&json); + * + * parcString_Release(&a); + * } + * @endcode + */ +PARCJSON *parcString_ToJSON(const PARCString *instance); + +/** + * Produce a null-terminated string representation of the specified `PARCString`. + * + * The result must be freed by the caller via {@link parcMemory_Deallocate}. + * + * @param [in] instance A pointer to a valid PARCString instance. + * + * @return NULL Cannot allocate memory. + * @return non-NULL A pointer to an allocated, null-terminated C string that must be deallocated via {@link parcMemory_Deallocate}. + * + * Example: + * @code + * { + * PARCString *a = parcString_Create(); + * + * char *string = parcString_ToString(a); + * + * parcString_Release(&a); + * + * parcMemory_Deallocate(&string); + * } + * @endcode + * + * @see parcString_Display + */ +char *parcString_ToString(const PARCString *instance); + +/** + * Get a pointer to the underlying nul-terminated sequence of bytes containing the string's value. + * + * @param [in] string A pointer to a valid PARCString instance. + * + * @return A pointer to the underlying nul-terminated sequence of bytes + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +const char *parcString_GetString(const PARCString *string); +#endif diff --git a/libparc/parc/algol/parc_Time.c b/libparc/parc/algol/parc_Time.c new file mode 100755 index 00000000..7cc358b9 --- /dev/null +++ b/libparc/parc/algol/parc_Time.c @@ -0,0 +1,154 @@ +/* + * 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 <stdio.h> +#include <string.h> +#include <time.h> +#include <sys/time.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_Time.h> +#include <parc/algol/parc_Memory.h> + +char * +parcTime_TimevalAsString(struct timeval timeval) +{ + char *string; + int nwritten = asprintf(&string, "%ld.%06ld", timeval.tv_sec, (long) timeval.tv_usec); + assertTrue(nwritten >= 0, "Error calling asprintf"); + + char *result = parcMemory_StringDuplicate(string, strlen(string)); + free(string); + return result; +} + +char * +parcTime_TimevalAsRFC3339(const struct timeval *utcTime, char result[64]) +{ + char tmbuf[64]; + struct tm theTime; + + struct tm *nowtm = gmtime_r(&utcTime->tv_sec, &theTime); + strftime(tmbuf, sizeof tmbuf, "%Y-%m-%dT%H:%M:%S", nowtm); + snprintf(result, 64, "%s.%06ldZ", tmbuf, (long) utcTime->tv_usec); + return result; +} + +char * +parcTime_TimevalAsISO8601(const struct timeval *utcTime, char result[64]) +{ + char tmbuf[64]; + struct tm theTime; + + struct tm *nowtm = gmtime_r(&utcTime->tv_sec, &theTime); + strftime(tmbuf, sizeof tmbuf, "%Y-%m-%d %H:%M:%S", nowtm); + snprintf(result, 64, "%s.%06ldZ", tmbuf, (long) utcTime->tv_usec); + return result; +} + +char * +parcTime_TimeAsRFC3339(const time_t utcTime, char result[64]) +{ + struct timeval theTime = { utcTime, 0 }; + + return parcTime_TimevalAsRFC3339(&theTime, result); +} + +char * +parcTime_NowAsRFC3339(char result[64]) +{ + struct timeval theTime; + gettimeofday(&theTime, NULL); + + return parcTime_TimevalAsRFC3339(&theTime, result); +} + +char * +parcTime_TimeAsISO8601(const time_t utcTime, char result[64]) +{ + struct timeval theTime = { utcTime, 0 }; + + return parcTime_TimevalAsISO8601(&theTime, result); +} + +char * +parcTime_NowAsISO8601(char result[64]) +{ + struct timeval theTime; + gettimeofday(&theTime, NULL); + + return parcTime_TimevalAsISO8601(&theTime, result); +} + +struct timeval +parcTime_TimevalAdd(const struct timeval *addend1, const struct timeval *addend2) +{ + struct timeval sum; + + sum.tv_sec = addend1->tv_sec + addend2->tv_sec; + sum.tv_usec = addend1->tv_usec + addend2->tv_usec; + if (sum.tv_usec >= 1000000) { + sum.tv_usec -= 1000000; + sum.tv_sec++; + } + return sum; +} + +struct timeval +parcTime_TimevalSubtract(const struct timeval *minuend, const struct timeval *subtrahend) +{ + struct timeval result; + + result.tv_sec = minuend->tv_sec - subtrahend->tv_sec; + result.tv_usec = minuend->tv_usec - subtrahend->tv_usec; + if (result.tv_usec < 0) { + result.tv_sec--; + result.tv_usec += 1000000; + } + return result; +} + +struct timeval +parcTime_NowTimeval(void) +{ + struct timeval timeval; + gettimeofday(&timeval, NULL); + return timeval; +} + +uint64_t +parcTime_NowMicroseconds(void) +{ + struct timeval timeval; + gettimeofday(&timeval, NULL); + + uint64_t result = timeval.tv_sec * 1000000 + timeval.tv_usec; + return result; +} + +uint64_t +parcTime_NowNanoseconds(void) +{ + struct timeval timeval; + gettimeofday(&timeval, NULL); + + uint64_t result = timeval.tv_sec * 1000000000 + timeval.tv_usec * 1000; + return result; +} diff --git a/libparc/parc/algol/parc_Time.h b/libparc/parc/algol/parc_Time.h new file mode 100644 index 00000000..2a68144e --- /dev/null +++ b/libparc/parc/algol/parc_Time.h @@ -0,0 +1,262 @@ +/* + * 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. + */ + +/** + * @file parc_Time.h + * @ingroup inputoutput + * @brief Time Manipulation + * + * Different platforms have different ways to express time-of-day, elapsed-time, clock-time. + * In some cases multiple ways to express the same semantic value have evolved over time, + * for example `struct timeval` and `struct timespec`. + * + */ +#ifndef libparc_parc_Time_h +#define libparc_parc_Time_h + +#include <sys/time.h> +#include <stdint.h> + +/** + * Create a nul-terminated C string containing the formatted representation of a `struct timeval`. + * + * The minimum length of the result is 8 characters consisting of the `struct timeval` + * formatted as a decimal string consisting of the number of seconds since midnight (0 hour), January 1, 1970. + * + * @param [in] timeval The instance of the struct `timeval` to convert to a C string. + * @return An allocated, null-terminated C string that must be freed via {@link parcMemory_Deallocate()}. + * + * Example: + * @code + * { + * struct timeval timeval; + * gettimeofday(&timeval, NULL); + * + * char *string = parcTime_TimevalAsString(timeval); + * + * parcMemory_Deallocate(&string); + * } + * @endcode + */ +char *parcTime_TimevalAsString(struct timeval timeval); + +/** + * Format an ISO8601 date from the given struct timeval into the given character array, @p result. + * + * @param [in] utcTime A pointer to a valid struct timeval instance with the time in UTC. + * @param [out] result A pointer to the 64 element nul-terminated character array, @p result. + * + * @return The same value as @p result. + * + * Example: + * @code + * { + * struct timeval theTime; + * gettimeofday(&theTime, NULL); + * + * char result[64]; + * parcTime_TimevalAsISO8601(&theTime, result); + * + * } + * @endcode + */ +char *parcTime_TimevalAsISO8601(const struct timeval *utcTime, char *result); + +/** + * Format an RFC3339 compliant date from the given struct timeval into the given character array. + * + * @param [in] utcTime A pointer to a valid struct timeval instance with the time in UTC. + * @param [out] result A pointer to the 64 element nul-terminated character array, @p result. + * + * @return The same value as @p result. + * + * Example: + * @code + * { + * struct timeval theTime; + * gettimeofday(&theTime, NULL); + * + * char result[64]; + * parcTime_TimevalAsRFC3339(&theTime, result); + * + * } + * @endcode + */ +char *parcTime_TimevalAsRFC3339(const struct timeval *utcTime, char *result); + +/** + * Format an ISO8601 date from the given `time_t` value into the given character array, @p result. + * + * @param [in] utcTime `time_t` value representing the time in UTC. + * @param [out] result A pointer to the 64 element nul-terminated character array, @p result. + * + * @return The same value as @p result. + * + * Example: + * @code + * { + * time_t theTime = time(0); + * + * char result[64]; + * parcTime_TimeAsISO8601(theTime, result); + * + * } + * @endcode + */ +char *parcTime_TimeAsISO8601(const time_t utcTime, char *result); + +/** + * Format the current time as an ISO8601 date into the given character array, @p result. + * + * @param [out] result A pointer to the 64 element nul-terminated character array, @p result. + * + * @return The same value as @p result. + * + * Example: + * @code + * { + * char result[64]; + * parcTime_NowAsISO8601(theTime, result); + * + * } + * @endcode + */ +char *parcTime_NowAsISO8601(char *result); + +/** + * Format an RFC3339 compliant date from the given `time_t` value into the given character array. + * + * @param [in] utcTime `time_t` value of the time in UTC. + * @param [out] result A pointer to the 64 element nul-terminated character array, @p result. + * + * @return The same value as @p result. + * + * Example: + * @code + * { + * struct timeval theTime; + * time_t theTime = time(0); + * + * char result[64]; + * parcTime_TimeAsRFC3339(&theTime, result); + * + * } + * @endcode + */ +char *parcTime_TimeAsRFC3339(const time_t utcTime, char *result); + +/** + * Format the current time as an RFC3339 compliant date into the given character array. + * + * @param [out] result A pointer to the 64 element nul-terminated character array, @p result. + * + * @return The same value as @p result. + * + * Example: + * @code + * { + * char result[64]; + * parcTime_NowAsRFC3339(&theTime, result); + * + * } + * @endcode + */ +char *parcTime_NowAsRFC3339(char *result); + +/** + * Add two `struct timeval` values together. + * + * @param [in] addend1 The first value. + * @param [in] addend2 The second value. + * + * @return The sum of the first and second values. + * + * Example: + * @code + * { + * struct timeval now; + * gettimeofday(&now, NULL); + * + * struct timeval theEnd = parcTime_TimevalAdd(&now, timeout); + * } + * @endcode + */ +struct timeval parcTime_TimevalAdd(const struct timeval *addend1, const struct timeval *addend2); + +/** + * Subtract two `struct timeval` values. + * + * @param [in] minuend The number from which the subtrahend is to be subtracted. + * @param [in] subtrahend The subtrahend. + * + * @return The difference between the first and second values. + * + * Example: + * @code + * { + * struct timeval now; + * gettimeofday(&now, NULL); + * + * struct timeval result = parcTime_TimevalSubtract(&now, timeout); + * } + * @endcode + */ +struct timeval parcTime_TimevalSubtract(const struct timeval *minuend, const struct timeval *subtrahend); + +/** + * The current time as a `struct timeval`. + * + * @return The current time as a `struct timeval`. + * + * Example: + * @code + * { + * + * struct timeval now = parcTime_NowTimeval(); + * } + * @endcode + */ +struct timeval parcTime_NowTimeval(void); + +/** + * The current time in microseconds since midnight (0 hour), January 1, 1970 as a `uint64_t`. + * + * @return The current time in microseconds since midnight (0 hour), January 1, 1970 as a `uint64_t`. + * + * Example: + * @code + * { + * + * uint64_t now = parcTime_NowMicroseconds(); + * } + * @endcode + */ +uint64_t parcTime_NowMicroseconds(void); + +/** + * The current time in nanoseconds since midnight (0 hour), January 1, 1970 as a `uint64_t`. + * + * @return The current time in nanoseconds since midnight (0 hour), January 1, 1970 as a `uint64_t`. + * + * Example: + * @code + * { + * + * uint64_t now = parcTime_NowNanoseconds(); + * } + * @endcode + */ +uint64_t parcTime_NowNanoseconds(void); +#endif // libparc_parc_Time_h diff --git a/libparc/parc/algol/parc_TreeMap.c b/libparc/parc/algol/parc_TreeMap.c new file mode 100755 index 00000000..9c776b30 --- /dev/null +++ b/libparc/parc/algol/parc_TreeMap.c @@ -0,0 +1,1120 @@ +/* + * 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 <LongBow/runtime.h> + +#include <stdio.h> + +#include "parc_TreeMap.h" +#include "parc_ArrayList.h" +#include "parc_KeyValue.h" + +#include <parc/algol/parc_Memory.h> + +#define RED 1 +#define BLACK 0 + +#define ASSERT_INVARIANTS + +struct treemap_node; +typedef struct treemap_node _RBNode; + +struct treemap_node { + _RBNode *leftChild; + _RBNode *rightChild; + _RBNode *parent; + PARCKeyValue *element; + int color; +}; + +struct parc_treemap { + _RBNode *root; + _RBNode *nil; + int size; + PARCTreeMap_CustomCompare *customCompare; +}; + +typedef void (rbRecursiveFunc)(_RBNode *node, PARCObject *data); + +static void +_rbNodeFree(_RBNode *node) +{ + if (node->element != NULL) { + parcKeyValue_Release(&(node->element)); + } + parcMemory_Deallocate((void **) &node); +} + +static void +_rbNodeFreeRecursive(PARCTreeMap *tree, _RBNode *node) +{ + if (node->leftChild != tree->nil) { + _rbNodeFreeRecursive(tree, node->leftChild); + } + if (node->rightChild != tree->nil) { + _rbNodeFreeRecursive(tree, node->rightChild); + } + // We descended on both branches, now free myself. + _rbNodeFree(node); + tree->size--; +} + +// Run a function on all nodes in the tree, in order +static void +_rbNodeRecursiveRun(PARCTreeMap *tree, _RBNode *node, rbRecursiveFunc *func, PARCObject *data) +{ + if (node->leftChild != tree->nil) { + _rbNodeRecursiveRun(tree, node->leftChild, func, data); + } + func(node, data); + if (node->rightChild != tree->nil) { + _rbNodeRecursiveRun(tree, node->rightChild, func, data); + } +} + + +static _RBNode * +_rbMinRelativeNode(const PARCTreeMap *tree, _RBNode *startNode) +{ + _RBNode *searchNode = startNode; + + // Let's get to the bottom left + while (searchNode->leftChild != tree->nil) { + searchNode = searchNode->leftChild; + } + + return searchNode; +} + +static _RBNode * +_rbMaxRelativeNode(const PARCTreeMap *tree, _RBNode *startNode) +{ + _RBNode *searchNode = startNode; + + // Let's get to the bottom left + while (searchNode->rightChild != tree->nil) { + searchNode = searchNode->rightChild; + } + + return searchNode; +} + +static _RBNode * +_rbNextNode(const PARCTreeMap *tree, _RBNode *node) +{ + _RBNode *searchNode = node; + if (searchNode->rightChild != tree->nil) { + searchNode = _rbMinRelativeNode(tree, searchNode->rightChild); + } else { + _RBNode *parent = searchNode->parent; + while (parent != tree->nil) { + if (parent->leftChild == searchNode) { + break; + } + searchNode = parent; + parent = searchNode->parent; + } + searchNode = parent; + } + + return searchNode; +} + +static _RBNode * +_rbPreviousNode(const PARCTreeMap *tree, _RBNode *node) +{ + _RBNode *searchNode = node; + if (searchNode->leftChild != tree->nil) { + searchNode = _rbMaxRelativeNode(tree, searchNode->leftChild); + } else { + _RBNode *parent = searchNode->parent; + while (parent != tree->nil) { + if (parent->rightChild == searchNode) { + break; + } + searchNode = parent; + parent = searchNode->parent; + } + searchNode = parent; + } + + return searchNode; +} + + +/** + * Create a node + * Set the parent and children to tree->nil. + * If we are creating the nil node this might leave garbage there (if not preset to NULL). + */ +static _RBNode * +_rbNodeCreate(PARCTreeMap *tree, int color) +{ + _RBNode *node = parcMemory_AllocateAndClear(sizeof(_RBNode)); + assertNotNull(node, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(_RBNode)); + node->color = color; + node->leftChild = tree->nil; + node->rightChild = tree->nil; + node->parent = tree->nil; + return node; +} + +static void +_rbNodeSetColor(_RBNode *node, uint8_t color) +{ + node->color = color; +} + +static int +_rbNodeColor(const _RBNode *node) +{ + return node->color; +} + +static bool +_rbNodeIsEqual(const PARCTreeMap *tree, const _RBNode *node, const PARCObject *key) +{ + bool result = false; + if (node->element != NULL) { + if (tree->customCompare != NULL) { + result = (tree->customCompare(parcKeyValue_GetKey(node->element), key) == 0); + } else { + result = parcObject_Equals(parcKeyValue_GetKey(node->element), key); + } + } + return result; +} + +static bool +_rbNodeIsGreaterThan(const PARCTreeMap *tree, const _RBNode *node, const PARCObject *key) +{ + bool result = false; + if (node->element != NULL) { + if (tree->customCompare != NULL) { + result = (tree->customCompare(parcKeyValue_GetKey(node->element), key) > 0); + } else { + result = (parcObject_Compare(parcKeyValue_GetKey(node->element), key) > 0); + } + } + return result; +} + +static _RBNode * +_rbFindNode(const PARCTreeMap *tree, _RBNode *startNode, const PARCObject *key) +{ + _RBNode *result = NULL; + _RBNode *node = startNode; + + // Let's get to the bottom of the tree to insert. + while (node != tree->nil) { + if (_rbNodeIsEqual(tree, node, key)) { + result = node; + break; + } else { + if (_rbNodeIsGreaterThan(tree, node, key)) { + node = node->leftChild; + } else { + node = node->rightChild; + } + } + } + return result; +} + + +static void +_rbNodeUpdate(_RBNode *treeNode, _RBNode *newNode) +{ + // Free old values + if (treeNode->element != NULL) { + parcKeyValue_Release(&treeNode->element); + } + + treeNode->element = parcKeyValue_Acquire(newNode->element); + _rbNodeFree(newNode); +} + +static void +_rbNodeRotateLeft(PARCTreeMap *tree, _RBNode *node) +{ + _RBNode *subroot = node->rightChild; + node->rightChild = subroot->leftChild; + if (node->rightChild != tree->nil) { + node->rightChild->parent = node; + } + + subroot->parent = node->parent; + if (tree->root == node) { + tree->root = subroot; + } else { + if (subroot->parent->leftChild == node) { + // node was a left child + subroot->parent->leftChild = subroot; + } else { + // node was a right child + subroot->parent->rightChild = subroot; + } + } + + subroot->leftChild = node; + node->parent = subroot; +} + +static void +_rbNodeRotateRight(PARCTreeMap *tree, _RBNode *node) +{ + _RBNode *subroot = node->leftChild; + node->leftChild = subroot->rightChild; + if (node->leftChild != tree->nil) { + node->leftChild->parent = node; + } + + subroot->parent = node->parent; + if (tree->root == node) { + tree->root = subroot; + } else { + if (subroot->parent->leftChild == node) { + // node was a left child + subroot->parent->leftChild = subroot; + } else { + // node was a right child + subroot->parent->rightChild = subroot; + } + } + + subroot->rightChild = node; + node->parent = subroot; +} + +static void +_rbNodeFix(PARCTreeMap *tree, _RBNode *startNode) +{ + _RBNode *node = startNode; + _RBNode *uncle; + while (_rbNodeColor(node->parent) == RED) { + if (node->parent->parent->leftChild == node->parent) { + uncle = node->parent->parent->rightChild; + if (_rbNodeColor(uncle) == RED) { + // My dad and uncle are red. Switch dad to black. + // Switch grandpa to red and start there. + _rbNodeSetColor(node->parent, BLACK); + _rbNodeSetColor(uncle, BLACK); + _rbNodeSetColor(node->parent->parent, RED); + node = node->parent->parent; + } else { + if (node->parent->rightChild == node) { + node = node->parent; + _rbNodeRotateLeft(tree, node); + } + _rbNodeSetColor(node->parent, BLACK); + _rbNodeSetColor(node->parent->parent, RED); + _rbNodeRotateRight(tree, node->parent->parent); + } + } else { + uncle = node->parent->parent->leftChild; + if (_rbNodeColor(uncle) == RED) { + // My dad and uncle are red. Switch dad to black. + // Switch grandpa to red and start there. + _rbNodeSetColor(node->parent, BLACK); + _rbNodeSetColor(uncle, BLACK); + _rbNodeSetColor(node->parent->parent, RED); + node = node->parent->parent; + } else { + if (node->parent->leftChild == node) { + node = node->parent; + _rbNodeRotateRight(tree, node); + } + _rbNodeSetColor(node->parent, BLACK); + _rbNodeSetColor(node->parent->parent, RED); + _rbNodeRotateLeft(tree, node->parent->parent); + } + } + } + _rbNodeSetColor(tree->root, BLACK); +} + +static void +_rbNodeAssertNodeInvariants(_RBNode *node, PARCObject *data) +{ + PARCTreeMap *tree = (PARCTreeMap *) data; + assertNotNull(node->parent, "Node has NULL parent"); + assertNotNull(node->leftChild, "Left child NULL"); + assertNotNull(node->rightChild, "Richt child NULL"); + if (node != tree->root) { + assertTrue(node->parent != tree->nil, "Paren't can't be nill for node!"); + // Don't need to compare to parent, they compared to us + } + assertNotNull(node->element, "We have a null element!!"); + assertNotNull(parcKeyValue_GetKey(node->element), "We have a null key!!"); + assertNotNull(parcKeyValue_GetValue(node->element), "We have a null value!!"); + if (node->leftChild != tree->nil) { + if (tree->customCompare != NULL) { + assertTrue(tree->customCompare(parcKeyValue_GetKey(node->element), parcKeyValue_GetKey(node->leftChild->element)) > 0, "Left child not smaller?"); + } else { + assertTrue(parcObject_Compare(parcKeyValue_GetKey(node->element), parcKeyValue_GetKey(node->leftChild->element)) > 0, "Left child not smaller?"); + } + } + if (node->rightChild != tree->nil) { + if (tree->customCompare != NULL) { + assertTrue(tree->customCompare(parcKeyValue_GetKey(node->element), parcKeyValue_GetKey(node->rightChild->element)) < 0, "Right child not bigger?"); + } else { + assertTrue(parcObject_Compare(parcKeyValue_GetKey(node->element), parcKeyValue_GetKey(node->rightChild->element)) < 0, "Right child not bigger?"); + } + } +} + +static +void +_rbNodeAssertTreeInvariants(const PARCTreeMap *tree) +{ + assertNotNull(tree, "Tree is null!"); + assertTrue(tree->size >= 0, "Tree has negative size"); + if (tree->size != 0) { + assertTrue(tree->root != tree->nil, "Tree size = %d > 0 but root is nil", tree->size); + assertNotNull(tree->root, "Tree size > 0 but root is NULL"); +#ifdef ASSERT_INVARIANTS + _rbNodeRecursiveRun((PARCTreeMap *) tree, tree->root, _rbNodeAssertNodeInvariants, (PARCObject *) tree); +#endif + } +} + +static void +_rbNodeFixDelete(PARCTreeMap *tree, _RBNode *node) +{ + _RBNode *fixNode; + + while ((node != tree->root) && (_rbNodeColor(node) == BLACK)) { + _rbNodeAssertTreeInvariants(tree); + if (node == node->parent->leftChild) { + fixNode = node->parent->rightChild; + if (_rbNodeColor(fixNode) == RED) { + _rbNodeSetColor(fixNode, BLACK); + _rbNodeSetColor(node->parent, RED); + _rbNodeRotateLeft(tree, node->parent); + fixNode = node->parent->rightChild; + } + if ((_rbNodeColor(fixNode->leftChild) == BLACK) && + (_rbNodeColor(fixNode->rightChild) == BLACK)) { + _rbNodeSetColor(fixNode, RED); + node = node->parent; + } else { + if (_rbNodeColor(fixNode->rightChild) == BLACK) { + _rbNodeSetColor(fixNode->leftChild, BLACK); + _rbNodeSetColor(fixNode, RED); + _rbNodeRotateRight(tree, fixNode); + fixNode = node->parent->rightChild; + } + _rbNodeSetColor(fixNode, _rbNodeColor(node->parent)); + _rbNodeSetColor(node->parent, BLACK); + _rbNodeSetColor(fixNode->rightChild, BLACK); + _rbNodeRotateLeft(tree, node->parent); + node = tree->root; + } + } else { + fixNode = node->parent->leftChild; + if (_rbNodeColor(fixNode) == RED) { + _rbNodeSetColor(fixNode, BLACK); + _rbNodeSetColor(node->parent, RED); + _rbNodeRotateRight(tree, node->parent); + fixNode = node->parent->leftChild; + } + if ((_rbNodeColor(fixNode->leftChild) == BLACK) && + (_rbNodeColor(fixNode->rightChild) == BLACK)) { + _rbNodeSetColor(fixNode, RED); + node = node->parent; + } else { + if (_rbNodeColor(fixNode->leftChild) == BLACK) { + _rbNodeSetColor(fixNode->rightChild, BLACK); + _rbNodeSetColor(fixNode, RED); + _rbNodeRotateLeft(tree, fixNode); + fixNode = node->parent->leftChild; + } + _rbNodeSetColor(fixNode, _rbNodeColor(node->parent)); + _rbNodeSetColor(node->parent, BLACK); + _rbNodeSetColor(fixNode->leftChild, BLACK); + _rbNodeRotateRight(tree, node->parent); + node = tree->root; + } + } + } + + _rbNodeSetColor(node, BLACK); +} + +// Remove the node from the tree. +// The node must be part of a tree (with parents and children) +static void +_rbNodeRemove(PARCTreeMap *tree, _RBNode *node) +{ + _rbNodeAssertTreeInvariants(tree); + _RBNode *fixupNode; + int deleteNodeColor = _rbNodeColor(node); + if (node->leftChild == tree->nil) { + if (node->rightChild == tree->nil) { + // ---- We have no children ---- + if (tree->root == node) { + tree->root = tree->nil; + } else { + if (node->parent->leftChild == node) { + node->parent->leftChild = tree->nil; + } else { + node->parent->rightChild = tree->nil; + } + } + fixupNode = tree->nil; + fixupNode->parent = node->parent; + } else { + // ---- We only have right child, move up ---- + if (tree->root == node) { + tree->root = node->rightChild; + } else { + if (node->parent->leftChild == node) { + node->parent->leftChild = node->rightChild; + } else { + node->parent->rightChild = node->rightChild; + } + } + fixupNode = node->rightChild; + node->rightChild->parent = node->parent; + } + } else { + if (node->rightChild == tree->nil) { + // ---- We only have left child, move up ---- + if (tree->root == node) { + tree->root = node->leftChild; + } else { + if (node->parent->leftChild == node) { + node->parent->leftChild = node->leftChild; + } else { + node->parent->rightChild = node->leftChild; + } + } + node->leftChild->parent = node->parent; + fixupNode = node->leftChild; + } else { + // ---- We have 2 children, move our successor to our location ---- + _RBNode *successor = node->rightChild; + while (successor->leftChild != tree->nil) { + successor = successor->leftChild; + } + deleteNodeColor = _rbNodeColor(successor); + + // Remove successor, it has no left child + if (successor == successor->parent->leftChild) { + successor->parent->leftChild = successor->rightChild; + } else { + successor->parent->rightChild = successor->rightChild; + } + successor->rightChild->parent = successor->parent; + + fixupNode = successor->rightChild; + + if (node->parent == tree->nil) { + tree->root = successor; + } else if (node->parent->leftChild == node) { + node->parent->leftChild = successor; + } else { + node->parent->rightChild = successor; + } + successor->parent = node->parent; + successor->leftChild = node->leftChild; + node->leftChild->parent = successor; + successor->rightChild = node->rightChild; + node->rightChild->parent = successor; + + _rbNodeSetColor(successor, _rbNodeColor(node)); + + if (successor->parent == tree->nil) { + tree->root = successor; + } + } + } + tree->size--; + + // Fix the red-blackness + _rbNodeAssertTreeInvariants(tree); + if (deleteNodeColor == BLACK) { + _rbNodeFixDelete(tree, fixupNode); + } + _rbNodeAssertTreeInvariants(tree); +} + +static void +_parcTreeMap_Destroy(PARCTreeMap **treePointer) +{ + assertNotNull(treePointer, "pointer to pointer to tree can't be null"); + assertNotNull(*treePointer, "pointer to tree can't be null"); + _rbNodeAssertTreeInvariants(*treePointer); + + if ((*treePointer)->size > 0) { + // If we have any elements in the tree, free them + _rbNodeFreeRecursive(*treePointer, (*treePointer)->root); + } + + // Free the nil element + parcMemory_Deallocate((void **) &((*treePointer)->nil)); +} + + +parcObject_ExtendPARCObject(PARCTreeMap, _parcTreeMap_Destroy, parcTreeMap_Copy, NULL, parcTreeMap_Equals, NULL, NULL, NULL); + +parcObject_ImplementAcquire(parcTreeMap, PARCTreeMap); + +parcObject_ImplementRelease(parcTreeMap, PARCTreeMap); + +PARCTreeMap * +parcTreeMap_CreateCustom(PARCTreeMap_CustomCompare *customCompare) +{ + PARCTreeMap *tree = parcObject_CreateInstance(PARCTreeMap); + assertNotNull(tree, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(PARCTreeMap)); + tree->nil = _rbNodeCreate(tree, BLACK); + tree->nil->leftChild = tree->nil; + tree->nil->rightChild = tree->nil; + tree->nil->parent = tree->nil; + tree->root = tree->nil; + tree->customCompare = customCompare; + tree->size = 0; + return tree; +} + +PARCTreeMap * +parcTreeMap_Create(void) +{ + return parcTreeMap_CreateCustom(NULL); +} + +void +parcTreeMap_Put(PARCTreeMap *tree, const PARCObject *key, const PARCObject *value) +{ + assertNotNull(tree, "Tree can't be NULL"); + assertNotNull(key, "Key can't be NULL"); + assertNotNull(value, "Value can't be NULL"); + + _RBNode *newNode = _rbNodeCreate(tree, RED); + _RBNode *parent = tree->nil; + _RBNode *node; + + // Set the value for the created node + PARCKeyValue *element = parcKeyValue_Create(key, value); + newNode->element = element; + + // Start at the top + node = tree->root; + + // Let's get to the bottom of the tree to insert. + while (node != tree->nil) { + parent = node; + if (_rbNodeIsEqual(tree, node, key)) { + // We're trying to insert the same value + _rbNodeUpdate(node, newNode); + return; + } else { + if (_rbNodeIsGreaterThan(tree, node, key)) { + // The key is smaller + node = node->leftChild; + } else { + node = node->rightChild; + } + } + } + + // We're at the bottom. + // node is nil (a leaf) + newNode->parent = parent; + if (parent == tree->nil) { + // nil is our parent, we are the root + tree->root = newNode; + } else { + if (_rbNodeIsGreaterThan(tree, parent, key)) { + parent->leftChild = newNode; + } else { + parent->rightChild = newNode; + } + } + + // We have inserted one node. + tree->size++; + + // We have a correct tree. But we need to regain the red-black property. + _rbNodeFix(tree, newNode); + + _rbNodeAssertTreeInvariants(tree); +} + +PARCObject * +parcTreeMap_Get(PARCTreeMap *tree, const PARCObject *key) +{ + assertNotNull(tree, "Tree can't be NULL"); + _rbNodeAssertTreeInvariants(tree); + + PARCObject *result = NULL; + + _RBNode *node = _rbFindNode(tree, tree->root, key); + + if (node != NULL) { + result = parcKeyValue_GetValue(node->element); + } + + return result; +} + +// Return value, remove from tree +PARCObject * +parcTreeMap_Remove(PARCTreeMap *tree, const PARCObject *key) +{ + assertNotNull(tree, "Tree can't be NULL"); + assertNotNull(key, "Key can't be NULL"); + _rbNodeAssertTreeInvariants(tree); + + PARCObject *result = NULL; + + _RBNode *node = _rbFindNode(tree, tree->root, key); + + if (node != NULL) { + _rbNodeRemove(tree, node); + result = parcObject_Acquire(parcKeyValue_GetValue(node->element)); + _rbNodeFree(node); + } + + // We didn't find the node + + _rbNodeAssertTreeInvariants(tree); + + return result; +} + +// remove from tree and destroy +void +parcTreeMap_RemoveAndRelease(PARCTreeMap *tree, const PARCObject *key) +{ + assertNotNull(tree, "Tree can't be NULL"); + assertNotNull(key, "Key can't be NULL"); + + _RBNode *node = _rbFindNode(tree, tree->root, key); + + if (node != NULL) { + _rbNodeRemove(tree, node); + _rbNodeFree(node); + } + + _rbNodeAssertTreeInvariants(tree); +} + +PARCKeyValue * +parcTreeMap_GetLastEntry(const PARCTreeMap *tree) +{ + assertNotNull(tree, "Tree can't be NULL"); + _rbNodeAssertTreeInvariants(tree); + _RBNode *node = tree->root; + + if (tree->size == 0) { + // We don't have any entries + return NULL; + } + + // Let's get to the bottom right of the tree to find the largest + while (node->rightChild != tree->nil) { + node = node->rightChild; + } + + return node->element; +} + +PARCObject * +parcTreeMap_GetLastKey(const PARCTreeMap *tree) +{ + PARCObject *result = NULL; + + PARCKeyValue *entry = parcTreeMap_GetLastEntry(tree); + if (entry != NULL) { + result = parcKeyValue_GetKey(entry); + } + + return result; +} + +PARCKeyValue * +parcTreeMap_GetFirstEntry(const PARCTreeMap *tree) +{ + assertNotNull(tree, "Tree can't be NULL"); + _rbNodeAssertTreeInvariants(tree); + + if (tree->size == 0) { + // We don't have any entries + return NULL; + } + + _RBNode *node = _rbMinRelativeNode(tree, tree->root); + + return node->element; +} + +PARCObject * +parcTreeMap_GetFirstKey(const PARCTreeMap *tree) +{ + PARCObject *result = NULL; + + PARCKeyValue *entry = parcTreeMap_GetFirstEntry(tree); + if (entry != NULL) { + result = parcKeyValue_GetKey(entry); + } + + return result; +} + +PARCKeyValue * +parcTreeMap_GetHigherEntry(const PARCTreeMap *tree, const PARCObject *key) +{ + PARCKeyValue *result = NULL; + + _RBNode *node = _rbFindNode(tree, tree->root, key); + if (node != NULL) { + node = _rbNextNode(tree, node); + if (node != NULL) { + result = node->element; + } + } + + return result; +} + +PARCObject * +parcTreeMap_GetHigherKey(const PARCTreeMap *tree, const PARCObject *key) +{ + PARCObject *result = NULL; + + PARCKeyValue *kv = parcTreeMap_GetHigherEntry(tree, key); + if (kv != NULL) { + result = parcKeyValue_GetKey(kv); + } + + return result; +} + +PARCKeyValue * +parcTreeMap_GetLowerEntry(const PARCTreeMap *tree, const PARCObject *key) +{ + PARCKeyValue *result = NULL; + + _RBNode *node = _rbFindNode(tree, tree->root, key); + if (node != NULL) { + node = _rbPreviousNode(tree, node); + if (node != NULL) { + result = node->element; + } + } + + return result; +} + +PARCObject * +parcTreeMap_GetLowerKey(const PARCTreeMap *tree, const PARCObject *key) +{ + PARCObject *result = NULL; + + PARCKeyValue *kv = parcTreeMap_GetLowerEntry(tree, key); + if (kv != NULL) { + result = parcKeyValue_GetKey(kv); + } + + return result; +} + + +size_t +parcTreeMap_Size(const PARCTreeMap *tree) +{ + assertNotNull(tree, "Tree can't be NULL"); + _rbNodeAssertTreeInvariants(tree); + + return tree->size; +} + +static void +_rbAddKeyToList(_RBNode *node, PARCList *list) +{ + parcList_Add(list, parcObject_Acquire(parcKeyValue_GetKey(node->element))); +} + +static void +_rbAddalueToList(_RBNode *node, PARCList *list) +{ + parcList_Add(list, parcObject_Acquire(parcKeyValue_GetValue(node->element))); +} + +static void +_rbAddElementToList(_RBNode *node, PARCList *list) +{ + parcList_Add(list, parcObject_Acquire(node->element)); +} + +PARCList * +parcTreeMap_AcquireKeys(const PARCTreeMap *tree) +{ + assertNotNull(tree, "Tree can't be NULL"); + _rbNodeAssertTreeInvariants(tree); + + PARCList *keys = parcList(parcArrayList_Create_Capacity((bool (*)(void *x, void *y))parcObject_Equals, + (void (*)(void **))parcObject_Release, tree->size), + PARCArrayListAsPARCList); + + if (tree->size > 0) { + _rbNodeRecursiveRun((PARCTreeMap *) tree, tree->root, (rbRecursiveFunc *) _rbAddKeyToList, keys); + } + return keys; +} + +PARCList * +parcTreeMap_AcquireValues(const PARCTreeMap *tree) +{ + assertNotNull(tree, "Tree can't be NULL"); + _rbNodeAssertTreeInvariants(tree); + + PARCList *values = parcList(parcArrayList_Create_Capacity((bool (*)(void *x, void *y))parcObject_Equals, + (void (*)(void **))parcObject_Release, tree->size), + PARCArrayListAsPARCList); + + if (tree->size > 0) { + _rbNodeRecursiveRun((PARCTreeMap *) tree, tree->root, (rbRecursiveFunc *) _rbAddalueToList, values); + } + return values; +} + +static PARCList * +_parcTreeMap_Elements(const PARCTreeMap *tree) +{ + assertNotNull(tree, "Tree can't be NULL"); + _rbNodeAssertTreeInvariants(tree); + + PARCList *elements = parcList(parcArrayList_Create_Capacity((bool (*)(void *x, void *y))parcObject_Equals, + (void (*)(void **))parcObject_Release, tree->size), + PARCArrayListAsPARCList); + + if (tree->size > 0) { + _rbNodeRecursiveRun((PARCTreeMap *) tree, tree->root, (rbRecursiveFunc *) _rbAddElementToList, elements); + } + return elements; +} + +bool +parcTreeMap_Equals(const PARCTreeMap *tree1, const PARCTreeMap *tree2) +{ + _rbNodeAssertTreeInvariants(tree1); + _rbNodeAssertTreeInvariants(tree2); + assertNotNull(tree1, "Tree can't be NULL"); + assertNotNull(tree2, "Tree can't be NULL"); + + bool result = false; + + PARCList *keys1 = parcTreeMap_AcquireKeys(tree1); + PARCList *keys2 = parcTreeMap_AcquireKeys(tree2); + size_t length1 = parcList_Size(keys1); + size_t length2 = parcList_Size(keys2); + + if (length1 == length2) { + result = true; + for (size_t i = 0; i < length1; i++) { + if (!parcObject_Equals(parcList_GetAtIndex(keys1, i), parcList_GetAtIndex(keys2, i))) { + result = false; + break; + } + } + if (result) { + PARCList *values1 = parcTreeMap_AcquireValues(tree1); + PARCList *values2 = parcTreeMap_AcquireValues(tree2); + size_t s1 = parcList_Size(values1); + size_t s2 = parcList_Size(values2); + if (s1 == s2) { + for (size_t i = 0; i < s1; i++) { + PARCObject *value1 = parcList_GetAtIndex(values1, i); + PARCObject *value2 = parcList_GetAtIndex(values2, i); + if (!parcObject_Equals(value1, value2)) { + result = false; + break; + } + } + } + parcList_Release(&values1); + parcList_Release(&values2); + } + } + + parcList_Release(&keys1); + parcList_Release(&keys2); + return result; +} + + +/* + * This is a simple implementation of Copy that goes through the list of keys and values. + */ +PARCTreeMap * +parcTreeMap_Copy(const PARCTreeMap *sourceTree) +{ + _rbNodeAssertTreeInvariants(sourceTree); + assertNotNull(sourceTree, "Tree can't be NULL"); + + PARCObject *keySource; + PARCObject *keyCopy; + PARCObject *valueSource; + PARCObject *valueCopy; + + PARCTreeMap *treeCopy = parcTreeMap_CreateCustom(sourceTree->customCompare); + + PARCList *keys = parcTreeMap_AcquireKeys(sourceTree); + PARCList *values = parcTreeMap_AcquireValues(sourceTree); + + size_t total_keys = parcList_Size(keys); + + for (size_t i = 0; i < total_keys; i++) { + keySource = parcList_GetAtIndex(keys, i); + valueSource = parcList_GetAtIndex(values, i); + + keyCopy = parcObject_Copy(keySource); + valueCopy = parcObject_Copy(valueSource); + + parcTreeMap_Put(treeCopy, keyCopy, valueCopy); + parcObject_Release(&keyCopy); + parcObject_Release(&valueCopy); + } + + parcList_Release(&keys); + parcList_Release(&values); + + return treeCopy; +} + +////// Iterator Support ////// + +typedef struct { + PARCTreeMap *map; + PARCList *list; + size_t currentIndex; + PARCKeyValue *currentElement; +} _PARCTreeMapIterator; + +static _PARCTreeMapIterator * +_parcTreeMapIterator_Init(PARCTreeMap *map) +{ + _PARCTreeMapIterator *state = parcMemory_AllocateAndClear(sizeof(_PARCTreeMapIterator)); + + if (state != NULL) { + state->map = map; + state->list = _parcTreeMap_Elements(map); + state->currentIndex = 0; + state->currentElement = parcList_GetAtIndex(state->list, 0); + trapOutOfMemoryIf(state->list == NULL, "Cannot create parcList"); + } + + return state; +} + +static bool +_parcTreeMapIterator_Fini(PARCTreeMap *map __attribute__((unused)), _PARCTreeMapIterator *state __attribute__((unused))) +{ + parcList_Release(&state->list); + parcMemory_Deallocate(&state); + return true; +} + +static _PARCTreeMapIterator * +_parcTreeMapIterator_Next(PARCTreeMap *map __attribute__((unused)), _PARCTreeMapIterator *state) +{ + state->currentElement = parcList_GetAtIndex(state->list, state->currentIndex); + ++state->currentIndex; + return state; +} + +static void +_parcTreeMapIterator_Remove(PARCTreeMap *map, _PARCTreeMapIterator **statePtr) +{ + _PARCTreeMapIterator *state = *statePtr; + + parcTreeMap_RemoveAndRelease(map, parcKeyValue_GetKey(state->currentElement)); +} + +static bool +_parcTreeMapIterator_HasNext(PARCTreeMap *map __attribute__((unused)), _PARCTreeMapIterator *state) +{ + return (parcList_Size(state->list) > state->currentIndex); +} + +static PARCObject * +_parcTreeMapIterator_Element(PARCTreeMap *map __attribute__((unused)), const _PARCTreeMapIterator *state) +{ + return state->currentElement; +} + +static PARCObject * +_parcTreeMapIterator_ElementValue(PARCTreeMap *map __attribute__((unused)), const _PARCTreeMapIterator *state) +{ + return parcKeyValue_GetValue(state->currentElement); +} + +static PARCObject * +_parcTreeMapIterator_ElementKey(PARCTreeMap *map __attribute__((unused)), const _PARCTreeMapIterator *state) +{ + return parcKeyValue_GetKey(state->currentElement); +} + +PARCIterator * +parcTreeMap_CreateValueIterator(PARCTreeMap *treeMap) +{ + PARCIterator *iterator = parcIterator_Create(treeMap, + (void *(*)(PARCObject *))_parcTreeMapIterator_Init, + (bool (*)(PARCObject *, void *))_parcTreeMapIterator_HasNext, + (void *(*)(PARCObject *, void *))_parcTreeMapIterator_Next, + (void (*)(PARCObject *, void **))_parcTreeMapIterator_Remove, + (void *(*)(PARCObject *, void *))_parcTreeMapIterator_ElementValue, + (void (*)(PARCObject *, void *))_parcTreeMapIterator_Fini, + NULL); + + return iterator; +} + + +PARCIterator * +parcTreeMap_CreateKeyIterator(PARCTreeMap *treeMap) +{ + PARCIterator *iterator = parcIterator_Create(treeMap, + (void *(*)(PARCObject *))_parcTreeMapIterator_Init, + (bool (*)(PARCObject *, void *))_parcTreeMapIterator_HasNext, + (void *(*)(PARCObject *, void *))_parcTreeMapIterator_Next, + (void (*)(PARCObject *, void **))_parcTreeMapIterator_Remove, + (void *(*)(PARCObject *, void *))_parcTreeMapIterator_ElementKey, + (void (*)(PARCObject *, void *))_parcTreeMapIterator_Fini, + NULL); + + return iterator; +} + +PARCIterator * +parcTreeMap_CreateKeyValueIterator(PARCTreeMap *treeMap) +{ + PARCIterator *iterator = parcIterator_Create(treeMap, + (void *(*)(PARCObject *))_parcTreeMapIterator_Init, + (bool (*)(PARCObject *, void *))_parcTreeMapIterator_HasNext, + (void *(*)(PARCObject *, void *))_parcTreeMapIterator_Next, + (void (*)(PARCObject *, void **))_parcTreeMapIterator_Remove, + (void *(*)(PARCObject *, void *))_parcTreeMapIterator_Element, + (void (*)(PARCObject *, void *))_parcTreeMapIterator_Fini, + NULL); + + return iterator; +} diff --git a/libparc/parc/algol/parc_TreeMap.h b/libparc/parc/algol/parc_TreeMap.h new file mode 100755 index 00000000..5f0ed8d8 --- /dev/null +++ b/libparc/parc/algol/parc_TreeMap.h @@ -0,0 +1,714 @@ +/* + * 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. + */ + +/** + * @file parc_TreeMap.h + * @ingroup datastructures + * @brief A Red-Black tree containing PARCObject keys and values. + * + * The map is sorted according to the natural ordering of its keys, + * or by a comparator function provided at creation time, depending on which constructor is used. + * + */ +#ifndef libparc_parc_TreeMap_h +#define libparc_parc_TreeMap_h + +#include <stdlib.h> + +#include "parc_Object.h" +#include "parc_KeyValue.h" +#include "parc_List.h" +#include "parc_Iterator.h" + +struct parc_treemap; +typedef struct parc_treemap PARCTreeMap; + +/** + * Definition of a custom funcion to compare two keys. A function of + * this signature can be provided to CreateCustom(...) constructor to + * override the default parcObject_Compare(...) for comparing key + * objects. This will be used during all internal comparisons. + * + * @param [in] key1 The first key to compare + * @param [in] key2 The second key to compare + * + * @return A signum comparison. negative if key1 is smaller than key2, + * 0 if equal, positive if key1 is bigger. + * + * Example: + * @code + * { + * int _compareKeys(const PARCObject *key1, const PARCObject *key2) {...} + * + * PARCTreeMap * tree1 = parcTreeMap_CreateCustom(_compareKeys); + * + * ... + * + * parcTreeMap_Release(&tree1); + * } + * @endcode + */ +typedef int (PARCTreeMap_CustomCompare)(const PARCObject *key1, const PARCObject *key2); + +/** + * Create a standard `PARCTreeMap` that uses parcObject_Compare for + * comparisons. + * + * @return NULL Error allocating memory + * @return Non-NULL An initialized TreeMap + * + * Example: + * @code + * { + * PARCTreeMap * tree1 = parcTreeMap_Create(.....); + * + * ... + * + * parcTreeMap_Release(&tree1); + * } + * @endcode + */ +PARCTreeMap *parcTreeMap_Create(void); + +/** + * Create a `PARCTreeMap` that uses the provided custom compare + * function for key comparisons. + * + * @param [in] customCompare A cusom function to compare keys (required) + * @return NULL Error allocating memory + * @return Non-NULL An initialized RedBlack tree + * + * Example: + * @code + * { + * int _compareKeys(const PARCObject *key1, const PARCObject *key2) {...} + * + * PARCTreeMap * tree1 = parcTreeMap_CreateCustom(_compareKeys); + * + * ... + * + * parcTreeMap_Release(&tree1); + * } + * @endcode + */ +PARCTreeMap *parcTreeMap_CreateCustom(PARCTreeMap_CustomCompare *customCompare); + +/** + * Acquire a reference to a `PARCTreeMap`. + * + * @param tree The tree reference to acquire. + * + * @return the acquired reference. + * + */ +PARCTreeMap *parcTreeMap_Acquire(const PARCTreeMap *tree); + +/** + * Release a reference to a `PARCTreeMap` object. If it is the last + * reference, the object itself is freed and the specified free + * function is invoked on the stored keys and values. + * + * @param [in,out] treePointer A pointer to a pointer to the `PARCTreeMap` to be released. + * + * Example: + * @code + * { + * PARCTreeMap * tree1 = parcTreeMap_Create(.....); + * + * ... + * + * parcTreeMap_Release(&tree1); + * } + * @endcode + */ +void parcTreeMap_Release(PARCTreeMap **treePointer); + +/** + * Insert a value into the `PARCTreeMap`. If the key exists in the + * tree then the new value will replace the old value. The old key and + * value will be released by the map and the map will acquire a + * reference to the new key and value. The key must not be NULL. + * + * @param [in, out] tree A pointer to an initialized `PARCTreeMap`. + * @param [in] key A pointer to a key - This may not be NULL. + * @param [in] value A pointer to a value. + * + * Example: + * @code + * { + * PARCTreeMap * tree1 = parcTreeMap_Create(.....); + * + * PARCObject *someKey = ...; + * PARCObject *someValue = ...; + * + * parcTreeMap_Put(tree1, someKey, someValue); + * + * parcObject_Release(&someKey); + * parcObject_Release(&someValue); + * parcTreeMap_Release(&tree1); + * } + * @endcode + */ +void parcTreeMap_Put(PARCTreeMap *tree, const PARCObject *key, const PARCObject *value); + +/** + * Checks to see if a PARCTreeMap already contains a key. + * + * @param [in] tree A pointer to an initialized `PARCTreeMap`. + * @param [in] key A pointer to a key - This may not be NULL. + * + * Example: + * @code + * { + * PARCTreeMap * tree1 = parcTreeMap_Create(.....); + * + * ... + * + * PARCObject *someKey = ...; + * + * if (parcTreeMap_ContainsKey(tree1, someKey) { + * ... + * } + * + * parcObject_Release(&someKey); + * parcTreeMap_Release(&tree1); + * } + * @endcode + */ +bool parcTreeMap_ContainsKey(PARCTreeMap *tree, PARCObject *key); + +/** + * Get a value from a `PARCTreeMap`. If the key is not found in the + * tree NULL is returned. The returned value will still be owned by + * the tree. + * + * @param [in] tree A pointer to an initialized `PARCTreeMap`. + * @param [in] key A pointer to a key (The tree will not own this). + * @return A pointer to a value. You do not own this value (it's still in the tree). + * + * Example: + * @code + * { + * PARCTreeMap * tree1 = parcTreeMap_Create(.....); + * + * ... + * + * PARCObject *someKey = ...; + * + * PARCObject *someValue = parcTreeMap_Get(tree1, someKey); + * if (someValue != NULL) { + * ... + * } + * + * parcObject_Release(&someKey); + * parcTreeMap_Release(&tree1); + * } + * @endcode + */ +PARCObject *parcTreeMap_Get(PARCTreeMap *tree, const PARCObject *key); + +/** + * Get the first (smallest) key from a `PARCTreeMap`. The returned key + * will still be owned by the tree. If the tree is empty the function + * will return NULL. + * + * @param [in] tree A pointer to an initialized `PARCTreeMap`. + * @return A pointer to the smallest key. + * + * Example: + * @code + * { + * PARCTreeMap * tree1 = parcTreeMap_Create(.....); + * + * ... + * + * PARCObject *someKey = parcTreeMap_GetFirstKey(tree1); + * if (someKey != NULL) { + * ... + * } + * + * parcTreeMap_Release(&tree1); + * } + * @endcode + */ +PARCObject *parcTreeMap_GetFirstKey(const PARCTreeMap *tree); + +/** + * Get the first entry (one with the smallest key) from a + * `PARCTreeMap`. The returned PARCKeyValue will still be owned by the + * tree. If the tree is empty the function will return NULL. + * + * @param [in] tree A pointer to an initialized `PARCTreeMap`. + * @return A pointer to the entry (a PARCKeyValue) with the smallest key. + * + * Example: + * @code + * { + * PARCTreeMap * tree1 = parcTreeMap_Create(.....); + * + * ... + * + * PARCKeyValue *someEntry = parcTreeMap_GetFirstEntry(tree1); + * if (someEntry != NULL) { + * ... + * } + * + * parcTreeMap_Release(&tree1); + * } + * @endcode + */ +PARCKeyValue *parcTreeMap_GetFirstEntry(const PARCTreeMap *tree); + +/** + * Get the last (largest) key from a `PARCTreeMap`. The returned key + * will still be owned by the tree. If the tree is empty the function + * will return NULL + * + * @param [in] tree A pointer to an initialized `PARCTreeMap`. + * @return A pointer to the largest key. + * + * Example: + * @code + * { + * PARCTreeMap * tree1 = parcTreeMap_Create(.....); + * + * ... + * + * PARCObject *someKey = parcTreeMap_GetLastKey(tree1); + * if (someKey != NULL) { + * ... + * } + * + * parcTreeMap_Release(&tree1); + * } + * @endcode + */ +PARCObject *parcTreeMap_GetLastKey(const PARCTreeMap *tree); + +/** + * Get the last entry (entry with the largest key) from a + * `PARCTreeMap`. The returned entry will still be owned by the tree. + * If the tree is empty the function will return NULL + * + * @param [in] tree A pointer to an initialized `PARCTreeMap`. + * @return A pointer to the entry (a PARCKeyValue) with the largest key. + * + * Example: + * @code + * { + * PARCTreeMap * tree1 = parcTreeMap_Create(.....); + * + * ... + * + * PARCKeyValue *lastEntry = parcTreeMap_GetLastEntry(tree1); + * if (lastEntry != NULL) { + * ... + * } + * + * parcTreeMap_Release(&tree1); + * } + * @endcode + */ +PARCKeyValue *parcTreeMap_GetLastEntry(const PARCTreeMap *tree); + +/** + * Get the next largest key from a `PARCTreeMap`. The returned key + * will still be owned by the tree. If the tree is empty or the + * supplied key is the largest, the function will return NULL + * + * @param [in] tree A pointer to an initialized `PARCTreeMap`. + * @return A pointer to the next key. You do not own this value (it's still in the tree). + * + * Example: + * @code + * { + * PARCTreeMap * tree1 = parcTreeMap_Create(.....); + * + * ... + * + * PARCObject *someKey = ...; + * + * PARCObject *nextKey = parcTreeMap_GetHigherKey(tree1, someKey); + * if (nextKey != NULL) { + * ... + * } + * + * parcObject_Release(&someKey); + * parcTreeMap_Release(&tree1); + * } + * @endcode + */ +PARCObject *parcTreeMap_GetHigherKey(const PARCTreeMap *tree, const PARCObject *key); + +/** + * Get the entry with the next largest key from a `PARCTreeMap`. The + * returned entry will still be owned by the tree. If the tree is + * empty or the supplied key is the largest, the function will return + * NULL. + * + * @param [in] tree A pointer to an initialized `PARCTreeMap`. + * @return A pointer to the next entry (a PARCKeyValue). The caller + * does not own this return. + * + * Example: + * @code + * { + * PARCTreeMap * tree1 = parcTreeMap_Create(.....); + * + * ... + * + * PARCObject *someKey = ...; + * + * PARCKeyValue *nextEntry = parcTreeMap_GetHigherEntry(tree1, someKey); + * if (nextEntry != NULL) { + * ... + * } + * + * parcObject_Release(&someKey); + * parcTreeMap_Release(&tree1); + * } + * @endcode + */ +PARCKeyValue *parcTreeMap_GetHigherEntry(const PARCTreeMap *tree, const PARCObject *key); + +/** + * Get the previous key from a `PARCTreeMap`. The returned key will + * still be owned by the tree. If the tree is empty or the supplied + * key is the smallest in the tree, the function will return NULL. + * + * @param [in] tree A pointer to an initialized `PARCTreeMap`. + * @param [in] key A pointer to an key + * @return A pointer to the previous key. The caller + * does not own this return. + * + * Example: + * @code + * { + * PARCTreeMap * tree1 = parcTreeMap_Create(.....); + * + * ... + * + * PARCObject *someKey = ...; + * + * PARCObject *previousKey = parcTreeMap_GetLowerKey(tree1, someKey); + * if (previousKey != NULL) { + * ... + * } + * + * parcObject_Release(&someKey); + * parcTreeMap_Release(&tree1); + * } + * @endcode + */ +PARCObject *parcTreeMap_GetLowerKey(const PARCTreeMap *tree, const PARCObject *key); + +/** + * Get the entry with the next smallest key from a `PARCTreeMap`. The returned entry (a PARCKeyValue) will + * still be owned by the tree. If the tree is empty or the supplied + * key is the smallest in the tree, the function will return NULL. + * + * @param [in] tree A pointer to an initialized `PARCTreeMap`. + * @param [in] key A pointer to an key + * @return A pointer to the previous entry (a PARCKeyValue). The caller + * does not own this return. + * + * Example: + * @code + * { + * PARCTreeMap * tree1 = parcTreeMap_Create(.....); + * + * ... + * + * PARCObject *someKey = ...; + * + * PARCKeyValue *previousEntry = parcTreeMap_GetLowerKey(tree1, someKey); + * if (previousEntry != NULL) { + * ... + * } + * + * parcObject_Release(&someKey); + * parcTreeMap_Release(&tree1); + * } + * @endcode + */ +PARCKeyValue *parcTreeMap_GetLowerEntry(const PARCTreeMap *tree, const PARCObject *key); + +/** + * Remove an entry from a `PARCTreeMap`. The entry will be removed + * from the tree and the tree's reference to the key will be + * released. The value will be returned and responsibility for + * releasing the reference to the value will transfer to the caller. + * The provided key will not be modified. If the key is not found in + * the tree, NULL is returned. + * + * @param [in,out] tree A pointer to an initialized `PARCTreeMap`. + * @param [in] key A pointer to a key (The tree will not own this). + * @return A pointer to a value or NULL. You will now own this value. + * + * Example: + * @code + * { + * PARCTreeMap * tree1 = parcTreeMap_Create(.....); + * + * ... + * + * PARCObject *someKey = ...; + * + * PARCObject *someValue = parcTreeMap_Remove(tree1, someKey); + * if (someValue != NULL) { + * ... + * parcObject_Release(&someValue); + * } + * + * parcObject_Release(&someKey); + * parcTreeMap_Release(&tree1); + * } + * @endcode + */ +PARCObject *parcTreeMap_Remove(PARCTreeMap *tree, const PARCObject *key); + +/** + * Remove and destroy an entry from a `PARCTreeMap`. The entry along with + * it's key & value will be removed and released. + * + * @param [in,out] tree A pointer to an initialized `PARCTreeMap`. + * @param [in] key A pointer to a key (The tree will not own this). + * + * Example: + * @code + * { + * PARCTreeMap * tree1 = parcTreeMap_Create(.....); + * + * ... + * + * PARCObject *someKey = ...; + * + * parcTreeMap_RemoveAndRelease(tree1, someKey); + * + * parcObject_Release(&someKey); + * parcTreeMap_Release(&tree1); + * } + * @endcode + */ +void parcTreeMap_RemoveAndRelease(PARCTreeMap *tree, const PARCObject *key); + +/** + * Get the size (nuber of elements) of a `PARCTreeMap`. + * + * + * @param [in] tree A pointer to an initialized `PARCTreeMap`. + * @return size of the tree (number of elements). + * + * Example: + * @code + * { + * PARCTreeMap * tree1 = parcTreeMap_Create(.....); + * + * ... + * + * if (parcTreeMap_Size(tree1) > 0) { + * ... + * } + * + * parcTreeMap_Release(&tree1); + * } + * @endcode + */ +size_t parcTreeMap_Size(const PARCTreeMap *tree); + +/** + * Get a PARCList of the keys from a `PARCTreeMap`. All keys will be + * valid keys in the `PARCTreeMap`. The caller will own the list of + * keys and should release it when done. The caller will not own the + * keys themselves. Note that if the tree is modified or destroyed + * the key pointers might no longer point to valid keys. The list of + * keys will be sorted as per the provided or key's default compare function. + * + * @param [in] tree A pointer to a `PARCTreeMap`. + * @return A list of (pointers to) keys. + * + * Example: + * @code + * { + * PARCTreeMap * tree1 = parcTreeMap_Create(.....); + * + * ... + * + * PARCList *keys = parcTreeMap_AcquireKeys(tree1); + * for (size_t i=0; i < parcList_Size(keys); ++i) { + * ... + * } + * + * parcList_Release(&keys); + * parcTreeMap_Release(&tree1); + * } + * @endcode + */ +PARCList *parcTreeMap_AcquireKeys(const PARCTreeMap *tree); + +/** + * Get an PARCList of the values in this `PARCTreeMap`. All of these + * values will be valid in the `PARCTreeMap`. The caller will own the + * list of values and should release it when done. The list of values + * will be sorted as per the provided or key's default compare + * function. + * + * @param [in] tree A pointer to a `PARCTreeMap`. + * @return A list of (pointers to) values. + * + * Example: + * @code + * { + * PARCTreeMap * tree1 = parcTreeMap_Create(.....); + * + * ... + * + * PARCList *values = parcTreeMap_AcquireValues(tree1); + * for (size_t i=0; i < parcList_Size(values); ++i) { + * ... + * } + * + * parcList_Release(&keys); + * parcTreeMap_Release(&tree1); + * } + * @endcode + */ +PARCList *parcTreeMap_AcquireValues(const PARCTreeMap *tree); + +/** + * Calculate if two `PARCTreeMap`s are equal. + * + * Two trees are equal if they have the same keys associated with the + * same values. The keys & values will be compared using the + * parcObject_Equals(...); + * + * @param [in] tree1 A pointer to a `PARCTreeMap`. + * @param [in] tree2 A pointer to another `PARCTreeMap`. + * @return true if the trees are equal, zero otherwise + * + * Example: + * @code + * { + * PARCTreeMap * tree1 = parcTreeMap_Create(.....); + * PARCTreeMap * tree2 = parcTreeMap_Create(.....); + * + * ... + * + * if (parcTreeMap_Equals(tree1, tree2)) { + * ... + * } + * + * parcTreeMap_Release(&tree1); + * parcTreeMap_Release(&tree2); + * } + * @endcode + */ +bool parcTreeMap_Equals(const PARCTreeMap *tree1, const PARCTreeMap *tree2); + +/** + * Copy a TreeMap. + * + * This will create a completely new tree. It will copy every key and + * every value using parcObject_Copy(...). + * + * @param [in] sourceTree A pointer to a `PARCTreeMap` to be copied + * @return NULL Error copying the tree. + * @return Non-NULL A copy of the `PARCTreeMap`. + * + * Example: + * @code + * { + * PARCTreeMap * source_tree = parcTreeMap_Create(.....); + * + * ... operations on tree ... + * + * PARCTreeMap * tree_copy = parcTreeMap_Copy(source_tree); + * + * ... operations on tree ... + * + * parcTreeMap_Release(&source_tree); + * parcTreeMap_Release(&tree_copy); + * } + * @endcode + */ +PARCTreeMap *parcTreeMap_Copy(const PARCTreeMap *sourceTree); + + +/** + * Create a new instance of PARCIterator that iterates through the keys of the specified `PARCTreeMap`. + * The returned iterator must be released via {@link parcIterator_Release}. + * + * @param [in] hashMap A pointer to a valid `PARCTreeMap`. + * + * @see parcIterator_Release + * Example: + * @code + * { + * PARCIterator *iterator = parcTreeMap_CreateKeyIterator(myTreeMap); + * + * while (parcIterator_HasNext(iterator)) { + * PARCObject *object = parcIterator_Next(iterator); + * } + * + * parcIterator_Release(&iterator); + * } + * @endcode + */ +PARCIterator *parcTreeMap_CreateKeyIterator(PARCTreeMap *tree); + +/** + * Create a new instance of PARCIterator that iterates through the values of the specified `PARCTreeMap`. + * The returned iterator must be released via {@link parcIterator_Release}. + * + * @param [in] hashMap A pointer to a valid `PARCTreeMap`. + * + * @see parcIterator_Release + * Example: + * @code + * { + * PARCIterator *iterator = parcTreeMap_CreateValueIterator(myTreeMap); + * + * while (parcIterator_HasNext(iterator)) { + * PARCObject *object = parcIterator_Next(iterator); + * } + * + * parcIterator_Release(&iterator); + * } + * @endcode + */ +PARCIterator *parcTreeMap_CreateValueIterator(PARCTreeMap *tree); + +/** + * Create a new instance of PARCIterator that iterates through the KeyValue elements of the specified `PARCTreeMap`. + * The returned iterator must be released via {@link parcIterator_Release}. + * + * @param [in] hashMap A pointer to a valid `PARCTreeMap`. + * + * @see parcIterator_Release + * Example: + * @code + * { + * PARCIterator *iterator = parcTreeMap_CreateKeyValueIterator(myTreeMap); + * + * while (parcIterator_HasNext(iterator)) { + * PARCObject *object = parcIterator_Next(iterator); + * } + * + * parcIterator_Release(&iterator); + * } + * @endcode + */ +PARCIterator *parcTreeMap_CreateKeyValueIterator(PARCTreeMap *tree); +#endif // libparc_parc_TreeMap_h diff --git a/libparc/parc/algol/parc_TreeRedBlack.c b/libparc/parc/algol/parc_TreeRedBlack.c new file mode 100755 index 00000000..05480d67 --- /dev/null +++ b/libparc/parc/algol/parc_TreeRedBlack.c @@ -0,0 +1,840 @@ +/* + * 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 <LongBow/runtime.h> + +#include <stdio.h> + +#include <parc/algol/parc_TreeRedBlack.h> + +#include <parc/algol/parc_Memory.h> + +#define RED 1 +#define BLACK 0 + +#define ASSERT_INVARIANTS + +struct redblack_node; +typedef struct redblack_node Node; + +struct redblack_node { + Node *left_child; + Node *right_child; + Node *parent; + void *key; + void *value; + int color; +}; + +struct parc_tree_redblack { + Node *root; + Node *nil; + int size; + PARCTreeRedBlack_KeyCompare *keyCompare; + PARCTreeRedBlack_KeyFree *keyFree; + PARCTreeRedBlack_KeyCopy *keyCopy; + PARCTreeRedBlack_ValueFree *valueFree; + PARCTreeRedBlack_ValueEquals *valueEquals; + PARCTreeRedBlack_ValueCopy *valueCopy; +}; + +typedef void (rbRecursiveFunc)(Node *node, void *data); + +static void +_rbNodeFree(PARCTreeRedBlack *tree, Node *node) +{ + if (tree->keyFree != NULL) { + tree->keyFree(&(node->key)); + } + if (tree->valueFree != NULL) { + tree->valueFree(&(node->value)); + } + parcMemory_Deallocate((void **) &node); +} + +static void +_rbNodeFreeRecursive(PARCTreeRedBlack *tree, Node *node) +{ + if (node->left_child != tree->nil) { + _rbNodeFreeRecursive(tree, node->left_child); + } + if (node->right_child != tree->nil) { + _rbNodeFreeRecursive(tree, node->right_child); + } + // We descended on both branches, now free myself. + _rbNodeFree(tree, node); + tree->size--; +} + +// Run a function on all nodes in the tree, in order +static void +_rbNodeRecursiveRun(PARCTreeRedBlack *tree, Node *node, rbRecursiveFunc *func, void *data) +{ + if (node->left_child != tree->nil) { + _rbNodeRecursiveRun(tree, node->left_child, func, data); + } + func(node, data); + if (node->right_child != tree->nil) { + _rbNodeRecursiveRun(tree, node->right_child, func, data); + } +} + +/** + * Create a node + * Set the parent and children to tree->nil. + * If we are creating the nil node this might leave garbage there (if not preset to NULL). + */ +static Node * +_rbNodeCreate(PARCTreeRedBlack *tree, int color) +{ + Node *node = parcMemory_AllocateAndClear(sizeof(Node)); + assertNotNull(node, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(Node)); + node->color = color; + node->left_child = tree->nil; + node->right_child = tree->nil; + node->parent = tree->nil; + return node; +} + +static void +_rbNodeSetColor(Node *node, uint8_t color) +{ + node->color = color; +} + +static int +_rbNodeColor(const Node *node) +{ + return node->color; +} + +static int +_rbNodeIsEqual(const PARCTreeRedBlack *tree, const Node *node, const void *key) +{ + return (tree->keyCompare(node->key, key) == 0); +} + +static int +_rbNodeIsGreaterThan(const PARCTreeRedBlack *tree, const Node *node, const void *key) +{ + return (tree->keyCompare(node->key, key) > 0); +} + +static void +_rbNodeUpdate(PARCTreeRedBlack *tree, Node *treeNode, Node *newNode) +{ + // Free old values + if (tree->keyFree != NULL) { + tree->keyFree(&(treeNode->key)); + } + if (tree->valueFree != NULL) { + tree->valueFree(&(treeNode->value)); + } + treeNode->key = newNode->key; + treeNode->value = newNode->value; + parcMemory_Deallocate((void **) &newNode); +} + +static void +_rbNodeRotateLeft(PARCTreeRedBlack *tree, Node *node) +{ + Node *subroot = node->right_child; + node->right_child = subroot->left_child; + if (node->right_child != tree->nil) { + node->right_child->parent = node; + } + + subroot->parent = node->parent; + if (tree->root == node) { + tree->root = subroot; + } else { + if (subroot->parent->left_child == node) { + // node was a left child + subroot->parent->left_child = subroot; + } else { + // node was a right child + subroot->parent->right_child = subroot; + } + } + + subroot->left_child = node; + node->parent = subroot; +} + +static void +_rbNodeRotateRight(PARCTreeRedBlack *tree, Node *node) +{ + Node *subroot = node->left_child; + node->left_child = subroot->right_child; + if (node->left_child != tree->nil) { + node->left_child->parent = node; + } + + subroot->parent = node->parent; + if (tree->root == node) { + tree->root = subroot; + } else { + if (subroot->parent->left_child == node) { + // node was a left child + subroot->parent->left_child = subroot; + } else { + // node was a right child + subroot->parent->right_child = subroot; + } + } + + subroot->right_child = node; + node->parent = subroot; +} + +static void +_rbNodeFix(PARCTreeRedBlack *tree, Node *startNode) +{ + Node *node = startNode; + Node *uncle; + while (_rbNodeColor(node->parent) == RED) { + if (node->parent->parent->left_child == node->parent) { + uncle = node->parent->parent->right_child; + if (_rbNodeColor(uncle) == RED) { + // My dad and uncle are red. Switch dad to black. + // Switch grandpa to red and start there. + _rbNodeSetColor(node->parent, BLACK); + _rbNodeSetColor(uncle, BLACK); + _rbNodeSetColor(node->parent->parent, RED); + node = node->parent->parent; + } else { + if (node->parent->right_child == node) { + node = node->parent; + _rbNodeRotateLeft(tree, node); + } + _rbNodeSetColor(node->parent, BLACK); + _rbNodeSetColor(node->parent->parent, RED); + _rbNodeRotateRight(tree, node->parent->parent); + } + } else { + uncle = node->parent->parent->left_child; + if (_rbNodeColor(uncle) == RED) { + // My dad and uncle are red. Switch dad to black. + // Switch grandpa to red and start there. + _rbNodeSetColor(node->parent, BLACK); + _rbNodeSetColor(uncle, BLACK); + _rbNodeSetColor(node->parent->parent, RED); + node = node->parent->parent; + } else { + if (node->parent->left_child == node) { + node = node->parent; + _rbNodeRotateRight(tree, node); + } + _rbNodeSetColor(node->parent, BLACK); + _rbNodeSetColor(node->parent->parent, RED); + _rbNodeRotateLeft(tree, node->parent->parent); + } + } + } + _rbNodeSetColor(tree->root, BLACK); +} + +static void +_rbNodeAssertNodeInvariants(Node *node, void *data) +{ + PARCTreeRedBlack *tree = (PARCTreeRedBlack *) data; + assertNotNull(node->parent, "Node has NULL parent"); + assertNotNull(node->left_child, "Left child NULL"); + assertNotNull(node->right_child, "Richt child NULL"); + if (node != tree->root) { + assertTrue(node->parent != tree->nil, "Paren't can't be nill for node!"); + // Don't need to compare to parent, they compared to us + } + assertNotNull(node->key, "We have a null key!!"); + assertNotNull(node->value, "We have a null value!!"); + if (node->left_child != tree->nil) { + assertTrue(tree->keyCompare(node->key, node->left_child->key) > 0, "Left child not smaller?"); + } + if (node->right_child != tree->nil) { + assertTrue(tree->keyCompare(node->key, node->right_child->key) < 0, "Right child not bigger?"); + } +} + +static +void +_rbNodeAssertTreeInvariants(const PARCTreeRedBlack *tree) +{ + assertNotNull(tree, "Tree is null!"); + assertTrue(tree->size >= 0, "Tree has negative size"); + assertNotNull(tree->keyCompare, "No key compare function"); + if (tree->size != 0) { + assertTrue(tree->root != tree->nil, "Tree size = %d > 0 but root is nil", tree->size); + assertNotNull(tree->root, "Tree size > 0 but root is NULL"); +#ifdef ASSERT_INVARIANTS + _rbNodeRecursiveRun((PARCTreeRedBlack *) tree, tree->root, _rbNodeAssertNodeInvariants, (void *) tree); +#endif + } +} + +static void +_rbNodeFixDelete(PARCTreeRedBlack *tree, Node *node) +{ + Node *fixNode; + + while ((node != tree->root) && (_rbNodeColor(node) == BLACK)) { + _rbNodeAssertTreeInvariants(tree); + if (node == node->parent->left_child) { + fixNode = node->parent->right_child; + if (_rbNodeColor(fixNode) == RED) { + _rbNodeSetColor(fixNode, BLACK); + _rbNodeSetColor(node->parent, RED); + _rbNodeRotateLeft(tree, node->parent); + fixNode = node->parent->right_child; + } + if ((_rbNodeColor(fixNode->left_child) == BLACK) && + (_rbNodeColor(fixNode->right_child) == BLACK)) { + _rbNodeSetColor(fixNode, RED); + node = node->parent; + } else { + if (_rbNodeColor(fixNode->right_child) == BLACK) { + _rbNodeSetColor(fixNode->left_child, BLACK); + _rbNodeSetColor(fixNode, RED); + _rbNodeRotateRight(tree, fixNode); + fixNode = node->parent->right_child; + } + _rbNodeSetColor(fixNode, _rbNodeColor(node->parent)); + _rbNodeSetColor(node->parent, BLACK); + _rbNodeSetColor(fixNode->right_child, BLACK); + _rbNodeRotateLeft(tree, node->parent); + node = tree->root; + } + } else { + fixNode = node->parent->left_child; + if (_rbNodeColor(fixNode) == RED) { + _rbNodeSetColor(fixNode, BLACK); + _rbNodeSetColor(node->parent, RED); + _rbNodeRotateRight(tree, node->parent); + fixNode = node->parent->left_child; + } + if ((_rbNodeColor(fixNode->left_child) == BLACK) && + (_rbNodeColor(fixNode->right_child) == BLACK)) { + _rbNodeSetColor(fixNode, RED); + node = node->parent; + } else { + if (_rbNodeColor(fixNode->left_child) == BLACK) { + _rbNodeSetColor(fixNode->right_child, BLACK); + _rbNodeSetColor(fixNode, RED); + _rbNodeRotateLeft(tree, fixNode); + fixNode = node->parent->left_child; + } + _rbNodeSetColor(fixNode, _rbNodeColor(node->parent)); + _rbNodeSetColor(node->parent, BLACK); + _rbNodeSetColor(fixNode->left_child, BLACK); + _rbNodeRotateRight(tree, node->parent); + node = tree->root; + } + } + } + + _rbNodeSetColor(node, BLACK); +} + +// Remove the node from the tree. +// The node must be part of a tree (with parents and children) +static void +_rbNodeRemove(PARCTreeRedBlack *tree, Node *node) +{ + _rbNodeAssertTreeInvariants(tree); + Node *fixupNode; + int deleteNodeColor = _rbNodeColor(node); + if (node->left_child == tree->nil) { + if (node->right_child == tree->nil) { + // ---- We have no children ---- + if (tree->root == node) { + tree->root = tree->nil; + } else { + if (node->parent->left_child == node) { + node->parent->left_child = tree->nil; + } else { + node->parent->right_child = tree->nil; + } + } + fixupNode = tree->nil; + fixupNode->parent = node->parent; + } else { + // ---- We only have right child, move up ---- + if (tree->root == node) { + tree->root = node->right_child; + } else { + if (node->parent->left_child == node) { + node->parent->left_child = node->right_child; + } else { + node->parent->right_child = node->right_child; + } + } + fixupNode = node->right_child; + node->right_child->parent = node->parent; + } + } else { + if (node->right_child == tree->nil) { + // ---- We only have left child, move up ---- + if (tree->root == node) { + tree->root = node->left_child; + } else { + if (node->parent->left_child == node) { + node->parent->left_child = node->left_child; + } else { + node->parent->right_child = node->left_child; + } + } + node->left_child->parent = node->parent; + fixupNode = node->left_child; + } else { + // ---- We have 2 children, move our successor to our location ---- + Node *successor = node->right_child; + while (successor->left_child != tree->nil) { + successor = successor->left_child; + } + deleteNodeColor = _rbNodeColor(successor); + + // Remove successor, it has no left child + if (successor == successor->parent->left_child) { + successor->parent->left_child = successor->right_child; + } else { + successor->parent->right_child = successor->right_child; + } + successor->right_child->parent = successor->parent; + + fixupNode = successor->right_child; + + if (node->parent == tree->nil) { + tree->root = successor; + } else if (node->parent->left_child == node) { + node->parent->left_child = successor; + } else { + node->parent->right_child = successor; + } + successor->parent = node->parent; + successor->left_child = node->left_child; + node->left_child->parent = successor; + successor->right_child = node->right_child; + node->right_child->parent = successor; + + _rbNodeSetColor(successor, _rbNodeColor(node)); + + if (successor->parent == tree->nil) { + tree->root = successor; + } + } + } + tree->size--; + + // Fix the red-blackness + _rbNodeAssertTreeInvariants(tree); + if (deleteNodeColor == BLACK) { + _rbNodeFixDelete(tree, fixupNode); + } + _rbNodeAssertTreeInvariants(tree); +} + + +PARCTreeRedBlack * +parcTreeRedBlack_Create(PARCTreeRedBlack_KeyCompare *keyCompare, + PARCTreeRedBlack_KeyFree *keyFree, + PARCTreeRedBlack_KeyCopy *keyCopy, + PARCTreeRedBlack_ValueEquals *valueEquals, + PARCTreeRedBlack_ValueFree *valueFree, + PARCTreeRedBlack_ValueCopy *valueCopy) +{ + assertNotNull(keyCompare, "We need a key compare function"); + PARCTreeRedBlack *tree = parcMemory_AllocateAndClear(sizeof(PARCTreeRedBlack)); + assertNotNull(tree, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(PARCTreeRedBlack)); + tree->nil = _rbNodeCreate(tree, BLACK); + tree->nil->left_child = tree->nil; + tree->nil->right_child = tree->nil; + tree->nil->parent = tree->nil; + tree->root = tree->nil; + tree->keyCompare = keyCompare; + tree->keyFree = keyFree; + tree->keyCopy = keyCopy; + tree->valueEquals = valueEquals; + tree->valueFree = valueFree; + tree->valueCopy = valueCopy; + tree->size = 0; + return tree; +} + +void +parcTreeRedBlack_Destroy(PARCTreeRedBlack **treePointer) +{ + assertNotNull(treePointer, "pointer to pointer to tree can't be null"); + assertNotNull(*treePointer, "pointer to tree can't be null"); + _rbNodeAssertTreeInvariants(*treePointer); + + if ((*treePointer)->size > 0) { + // If we have any elements in the tree, free them + _rbNodeFreeRecursive(*treePointer, (*treePointer)->root); + } + + // Free the nil element + parcMemory_Deallocate((void **) &((*treePointer)->nil)); + + parcMemory_Deallocate((void **) treePointer); + *treePointer = NULL; +} + +void +parcTreeRedBlack_Insert(PARCTreeRedBlack *tree, void *key, void *value) +{ + assertNotNull(tree, "Tree can't be NULL"); + assertNotNull(key, "Key can't be NULL"); + assertNotNull(value, "Value can't be NULL"); + + Node *newNode = _rbNodeCreate(tree, RED); + Node *parent = tree->nil; + Node *node; + + // Set the value for the created node + newNode->key = key; + newNode->value = value; + + // Start at the top + node = tree->root; + + // Let's get to the bottom of the tree to insert. + while (node != tree->nil) { + parent = node; + if (_rbNodeIsEqual(tree, node, key)) { + // We're trying to insert the same value + _rbNodeUpdate(tree, node, newNode); + return; + } else { + if (_rbNodeIsGreaterThan(tree, node, key)) { + // The key is smaller + node = node->left_child; + } else { + node = node->right_child; + } + } + } + + // We're at the bottom. + // node is nil (a leaf) + newNode->parent = parent; + if (parent == tree->nil) { + // nil is our parent, we are the root + tree->root = newNode; + } else { + if (_rbNodeIsGreaterThan(tree, parent, key)) { + parent->left_child = newNode; + } else { + parent->right_child = newNode; + } + } + + // We have inserted one node. + tree->size++; + + // We have a correct tree. But we need to regain the red-black property. + _rbNodeFix(tree, newNode); + + _rbNodeAssertTreeInvariants(tree); +} + +// Return value +void * +parcTreeRedBlack_Get(PARCTreeRedBlack *tree, const void *key) +{ + assertNotNull(tree, "Tree can't be NULL"); + _rbNodeAssertTreeInvariants(tree); + + Node *node = tree->root; + + // Let's get to the bottom of the tree to insert. + while (node != tree->nil) { + if (_rbNodeIsEqual(tree, node, key)) { + // We found the node, return + return node->value; + } else { + if (_rbNodeIsGreaterThan(tree, node, key)) { + node = node->left_child; + } else { + node = node->right_child; + } + } + } + return NULL; +} + +// Return value, remove from tree +void * +parcTreeRedBlack_Remove(PARCTreeRedBlack *tree, const void *key) +{ + assertNotNull(tree, "Tree can't be NULL"); + assertNotNull(key, "Key can't be NULL"); + _rbNodeAssertTreeInvariants(tree); + + Node *node = tree->root; + + // Let's get to the bottom of the tree to insert. + while (node != tree->nil) { + if (_rbNodeIsEqual(tree, node, key)) { + _rbNodeRemove(tree, node); + void *value = node->value; + if (tree->keyFree != NULL) { + tree->keyFree(&node->key); + } + parcMemory_Deallocate((void **) &node); + _rbNodeAssertTreeInvariants(tree); + return value; + } else { + if (_rbNodeIsGreaterThan(tree, node, key)) { + node = node->left_child; + } else { + node = node->right_child; + } + } + } + + // We didn't find the node + + _rbNodeAssertTreeInvariants(tree); + return NULL; +} + +// remove from tree and destroy +void +parcTreeRedBlack_RemoveAndDestroy(PARCTreeRedBlack *tree, const void *key) +{ + assertNotNull(tree, "Tree can't be NULL"); + assertNotNull(key, "Key can't be NULL"); + + Node *node = tree->root; + // Let's get to the bottom of the tree to insert. + while (node != tree->nil) { + if (_rbNodeIsEqual(tree, node, key)) { + _rbNodeRemove(tree, node); + _rbNodeFree(tree, node); + _rbNodeAssertTreeInvariants(tree); + return; + } else { + if (_rbNodeIsGreaterThan(tree, node, key)) { + node = node->left_child; + } else { + node = node->right_child; + } + } + } + _rbNodeAssertTreeInvariants(tree); +} + +void * +parcTreeRedBlack_LastKey(const PARCTreeRedBlack *tree) +{ + assertNotNull(tree, "Tree can't be NULL"); + _rbNodeAssertTreeInvariants(tree); + Node *node = tree->root; + + if (tree->size == 0) { + // We don't have any entries + return NULL; + } + + // Let's get to the bottom right of the tree to find the largest + while (node->right_child != tree->nil) { + node = node->right_child; + } + + return node->key; +} + +void * +parcTreeRedBlack_FirstKey(const PARCTreeRedBlack *tree) +{ + assertNotNull(tree, "Tree can't be NULL"); + _rbNodeAssertTreeInvariants(tree); + Node *node = tree->root; + + + if (tree->size == 0) { + // We don't have any entries + return NULL; + } + + // Let's get to the bottom left of the tree to find the smallest + while (node->left_child != tree->nil) { + node = node->left_child; + } + + return node->key; +} + +size_t +parcTreeRedBlack_Size(const PARCTreeRedBlack *tree) +{ + assertNotNull(tree, "Tree can't be NULL"); + _rbNodeAssertTreeInvariants(tree); + + return tree->size; +} + +static void +_rbGetKeys(Node *node, void *data) +{ + PARCArrayList *list = (PARCArrayList *) data; + parcArrayList_Add(list, node->key); +} + +PARCArrayList * +parcTreeRedBlack_Keys(const PARCTreeRedBlack *tree) +{ + assertNotNull(tree, "Tree can't be NULL"); + _rbNodeAssertTreeInvariants(tree); + + PARCArrayList *keys = parcArrayList_Create(NULL); + + if (tree->size > 0) { + _rbNodeRecursiveRun((PARCTreeRedBlack *) tree, tree->root, _rbGetKeys, keys); + } + return keys; +} + +static void +_rbGetValues(Node *node, void *data) +{ + PARCArrayList *list = (PARCArrayList *) data; + parcArrayList_Add(list, node->value); +} + +PARCArrayList * +parcTreeRedBlack_Values(const PARCTreeRedBlack *tree) +{ + assertNotNull(tree, "Tree can't be NULL"); + _rbNodeAssertTreeInvariants(tree); + + PARCArrayList *values = parcArrayList_Create(NULL); + + if (tree->size > 0) { + _rbNodeRecursiveRun((PARCTreeRedBlack *) tree, tree->root, _rbGetValues, values); + } + return values; +} + +int +parcTreeRedBlack_Equals(const PARCTreeRedBlack *tree1, const PARCTreeRedBlack *tree2) +{ + _rbNodeAssertTreeInvariants(tree1); + _rbNodeAssertTreeInvariants(tree2); + assertNotNull(tree1, "Tree can't be NULL"); + assertNotNull(tree2, "Tree can't be NULL"); + + int ret = 1; + + PARCArrayList *keys1 = parcTreeRedBlack_Keys(tree1); + PARCArrayList *keys2 = parcTreeRedBlack_Keys(tree2); + size_t length1 = parcArrayList_Size(keys1); + size_t length2 = parcArrayList_Size(keys2); + + if (length1 == length2) { + for (size_t i = 0; i < length1; i++) { + if (tree1->keyCompare(parcArrayList_Get(keys1, i), parcArrayList_Get(keys2, i)) != 0) { + ret = 0; + break; + } + } + if (ret != 0) { + PARCArrayList *values1 = parcTreeRedBlack_Values(tree1); + PARCArrayList *values2 = parcTreeRedBlack_Values(tree2); + size_t length1 = parcArrayList_Size(values1); + size_t length2 = parcArrayList_Size(values2); + if (length1 == length2) { + for (size_t i = 0; i < length1; i++) { + void *value1 = parcArrayList_Get(values1, i); + void *value2 = parcArrayList_Get(values2, i); + if (tree1->valueEquals != NULL) { + if (tree1->valueEquals(value1, value2) == 0) { + ret = 0; + break; + } + } else { + if (value1 != value2) { + ret = 0; + break; + } + } + } + } + parcArrayList_Destroy(&values1); + parcArrayList_Destroy(&values2); + } + } else { + ret = 0; + } + + parcArrayList_Destroy(&keys1); + parcArrayList_Destroy(&keys2); + return ret; +} + + +/* + * This is a simple implementation of Copy that goes through the list of keys and values. + */ +PARCTreeRedBlack * +parcTreeRedBlack_Copy(const PARCTreeRedBlack *source_tree) +{ + _rbNodeAssertTreeInvariants(source_tree); + assertNotNull(source_tree, "Tree can't be NULL"); + + void *key_source; + void *key_copy; + void *value_source; + void *value_copy; + + PARCTreeRedBlack *tree_copy = parcTreeRedBlack_Create(source_tree->keyCompare, + source_tree->keyFree, + source_tree->keyCopy, + source_tree->valueEquals, + source_tree->valueFree, + source_tree->valueCopy); + + PARCArrayList *keys = parcTreeRedBlack_Keys(source_tree); + PARCArrayList *values = parcTreeRedBlack_Values(source_tree); + + size_t total_keys = parcArrayList_Size(keys); + + for (size_t i = 0; i < total_keys; i++) { + key_source = parcArrayList_Get(keys, i); + value_source = parcArrayList_Get(values, i); + + if (source_tree->keyCopy != NULL) { + key_copy = source_tree->keyCopy(key_source); + } else { + key_copy = key_source; + } + + if (source_tree->valueCopy != NULL) { + value_copy = source_tree->valueCopy(value_source); + } else { + value_copy = value_source; + } + + parcTreeRedBlack_Insert(tree_copy, key_copy, value_copy); + } + + parcArrayList_Destroy(&keys); + parcArrayList_Destroy(&values); + + return tree_copy; +} diff --git a/libparc/parc/algol/parc_TreeRedBlack.h b/libparc/parc/algol/parc_TreeRedBlack.h new file mode 100755 index 00000000..52d4af36 --- /dev/null +++ b/libparc/parc/algol/parc_TreeRedBlack.h @@ -0,0 +1,384 @@ +/* + * 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. + */ + +/** + * @file parc_TreeRedBlack.h + * @ingroup datastructures + * @brief A red-black tree is a type of self-balancing binary search tree, + * a data structure used in computer science, typically used to implement associative arrays. + * + */ +#ifndef libparc_parc_TreeRedBlack_h +#define libparc_parc_TreeRedBlack_h + +#include <stdlib.h> +#include <parc/algol/parc_ArrayList.h> + +struct parc_tree_redblack; +typedef struct parc_tree_redblack PARCTreeRedBlack; + +/** + * Compare two keys (signum) + * + * This is the definition of a function that takes two keys and compares them. + * It is a signum function. A user of the RedBlack tree will provide this function on creation + * so the tree can perform it's operations. + * + * @param [in] key1 The first key to compare + * @param [in] key2 The second key to compare + * @return A signum comparison. negative if key 1 is smaller, 0 if key1 == key2, greater than 0 if key1 is bigger. + * + * Example: + * @code + * <#example#> + * @endcode + */ +typedef int (PARCTreeRedBlack_KeyCompare)(const void *key1, const void *key2); + +/** + * Compare two values for equality + * + * This is a function definiton for a function that compares two values in a `PARCTreeRedBlack`. + * If the values are equal it will return true. If they are not it will return false. + * A function of this type will be given on the creation of the `PARCTreeRedBlack`. It will be used by the tree + * to test equality. + * + * @param [in] value1 The first value to compare + * @param [in] value2 The second value to compare + * @return TRUE if the values are considered equal, FALSE if they are not + * + * Example: + * @code + * <#example#> + * @endcode + */ +typedef bool (PARCTreeRedBlack_ValueEquals)(const void *value1, const void *value2); + +/** + * A function to free a value pointer + * + * This is a function definition for a function to free a value in the `PARCTreeRedBlack`. The function should + * free/destroy the value when called. A function of this type will be passed to the `PARCTreeRedBlack` on + * creation. It will be used to destroy values when needed. + * + * @param [in,out] value A pointer to a pointer to a value to be freed. + * + * Example: + * @code + * <#example#> + * @endcode + */ +typedef void (PARCTreeRedBlack_ValueFree)(void **value); + +/** + * A function to free a key pointer + * + * This is a function definition for a function to free a key in the `PARCTreeRedBlack`. The function should + * free/destroy the key when called. A function of this type will be passed to the `PARCTreeRedBlack` on + * creation. It will be used to destroy keys when needed. + * + * @param [in,out] key A pointer to a pointer to a key to be freed. + * + * Example: + * @code + * <#example#> + * @endcode + */ +typedef void (PARCTreeRedBlack_KeyFree)(void **key); + +/** + * Create a copy of a key. + * + * This must be a real copy! (allocating new memory) + * + * This function should create a copy of the key. The copy must pass an equality test to the original key. + * If this function retuns NULL, the tree copy will fail (and return NULL) + * + * @param [in] key A pointer to the key to be copied + * + * @return NULL Memory could not be allocated. + * @return non-NULL A pointer to a new key instance + * + */ +typedef void * (PARCTreeRedBlack_KeyCopy)(const void *key); + +/** + * Create a copy of a value + * + * This must be a real copy! (new memory allocated) + * + * This function should create a copy of the value. The copy must pass an equality test to the original value. + * If this function retuns NULL, the tree copy will fail (and return NULL) + * + * @param [in] value A pointer to the value to be copied + * + * @return NULL Memory could not be allocated. + * @return non-NULL A pointer to a new value instance + * + */ +typedef void * (PARCTreeRedBlack_ValueCopy)(const void *value); + +/** + * Create a `PARCTreeRedBlack` + * + * Create a RedBlack Tree. You must provide a key compare function. + * The valueEquals function will be used to compare 2 trees. If you do not provide such a function + * the values will be compared directly. + * The keyFree and valueFree functions are optional but highly encouraged. They will be used on + * destruction of the tree on any remaining elements. They are also used on deletion of elements. + * + * @param [in] keyCompare A function to compare keys (required) + * @param [in] keyFree A function to free Keys (may be NULL) + * @param [in] keyCopy A function to copy Keys (may be NULL) + * @param [in] valueEquals A function to test value equality (may be NULL) + * @param [in] valueFree A function to free Values (may be NULL) + * @param [in] valueCopy A function to free Keys (may be NULL) + * @return NULL Error allocating memory + * @return Non-NULL An initialized RedBlack tree + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCTreeRedBlack *parcTreeRedBlack_Create(PARCTreeRedBlack_KeyCompare *keyCompare, + PARCTreeRedBlack_KeyFree *keyFree, + PARCTreeRedBlack_KeyCopy *keyCopy, + PARCTreeRedBlack_ValueEquals *valueEquals, + PARCTreeRedBlack_ValueFree *valueFree, + PARCTreeRedBlack_ValueCopy *valueCopy); + +/** + * Destroy a `PARCTreeRedBlack` + * + * Destroy a `PARCTreeRedBlack`, free all the memory. + * All remaining keys and values will be freed with the respective free functions. + * + * @param [in,out] treePointer A pointer to a pointer to the `PARCTreeRedBlack` to be destroyed. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void parcTreeRedBlack_Destroy(PARCTreeRedBlack **treePointer); + +/** + * Insert a value into the `PARCTreeRedBlack`. + * + * Insert a value into the `PARCTreeRedBlack`. + * If the key exists in the tree then the new value will replace the old value. + * The old key and value will be freed using the provided free functions. + * The tree will take ownership of the key and value. They can't be NULL. + * + * @param [in,out] tree A pointer to an initialized `PARCTreeRedBlack`. + * @param [in] key A pointer to a key - This may not be NULL. + * @param [in] value A pointer to a value - This may not be NULL. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void parcTreeRedBlack_Insert(PARCTreeRedBlack *tree, void *key, void *value); + +/** + * Get a value into the `PARCTreeRedBlack`. + * + * Get a value from a `PARCTreeRedBlack`. + * If the key is not found in the tree NULL is returned. + * The returned value will still be owned by the tree. + * + * @param [in] tree A pointer to an initialized `PARCTreeRedBlack`. + * @param [in] key A pointer to a key (The tree will not own this). + * @return A pointer to a value. You do not own this value (it's still in the tree). + * + * Example: + * @code + * <#example#> + * @endcode + */ +void *parcTreeRedBlack_Get(PARCTreeRedBlack *tree, const void *key); + +/** + * Get the first (smallest) key from a `PARCTreeRedBlack`. + * + * Get the first (smallest) key from a `PARCTreeRedBlack`. + * The returned key will still be owned by the tree. + * If the tree is empty the function will return NULL. + * + * @param [in] tree A pointer to an initialized `PARCTreeRedBlack`. + * @return A pointer to the smallest key. You do not own this value (it's still in the tree). + * + * Example: + * @code + * <#example#> + * @endcode + */ +void *parcTreeRedBlack_FirstKey(const PARCTreeRedBlack *tree); + +/** + * Get the last (largest) key from a `PARCTreeRedBlack`. + * + * Get the last (largest) key from a `PARCTreeRedBlack`. + * The returned key will still be owned by the tree. + * If the tree is empty the function will return NULL + * + * @param [in] tree A pointer to an initialized `PARCTreeRedBlack`. + * @return A pointer to the smallest key. You do not own this value (it's still in the tree). + * + * Example: + * @code + * <#example#> + * @endcode + */ +void *parcTreeRedBlack_LastKey(const PARCTreeRedBlack *tree); + +/** + * Remove a value (and key) from a `PARCTreeRedBlack`. + * + * Remove a value from a `PARCTreeRedBlack`. The value (and key) will no longer be in the tree. + * The (internal) key will be freed. The provided key will not be modified. + * The value associated with the key will be returned. You will own this value. + * If the key is not found in the tree NULL is returned. + * + * @param [in,out] tree A pointer to an initialized `PARCTreeRedBlack`. + * @param [in] key A pointer to a key (The tree will not own this). + * @return A pointer to a value. You will now own this value. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void *parcTreeRedBlack_Remove(PARCTreeRedBlack *tree, const void *key); + +/** + * Remove and destroy a value (and key) from a `PARCTreeRedBlack`. + * + * Delete a value from a `PARCTreeRedBlack`. The value (and key) will no longer be in the tree. + * The value and key will be freed by the tree. The provided key will not be modified. + * + * @param [in,out] tree A pointer to an initialized `PARCTreeRedBlack`. + * @param [in] key A pointer to a key (The tree will not own this). + * + * Example: + * @code + * <#example#> + * @endcode + */ +void parcTreeRedBlack_RemoveAndDestroy(PARCTreeRedBlack *tree, const void *key); + +/** + * Get the size of a `PARCTreeRedBlack`. + * + * Return the size of the tree (number of elements). + * + * @param [in] tree A pointer to an initialized `PARCTreeRedBlack`. + * @return size of the tree (number of elements). + * + * Example: + * @code + * <#example#> + * @endcode + */ +size_t parcTreeRedBlack_Size(const PARCTreeRedBlack *tree); + +/** + * Get an ArrayList of the keys in this tree + * + * Get a list of the keys from this `PARCTreeRedBlack`. All of these keys will be valid keys in the `PARCTreeRedBlack`. + * The caller will own the list of keys and should destroy it when done. The caller will not own + * the keys themselves. Destroying the {@link PARCArrayList} should be enough. + * Note that if the tree is modified or destroyed the key pointers might no longer point to valid keys. + * The list of keys will be sorted (as defined by the key compare function, smallest first) + * + * @param [in] tree A pointer to a `PARCTreeRedBlack`. + * @return A list of (pointers to) keys. + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCArrayList *parcTreeRedBlack_Keys(const PARCTreeRedBlack *tree); + +/** + * Get an ArrayList of the values in this `PARCTreeRedBlack`. + * + * Get a list of the values from this tree. All of these values will be valid in the `PARCTreeRedBlack`. + * The caller will own the list of values and should destroy it when done. The caller will not own + * the values themselves. Destroying the {@link PARCArrayList} should be enough. + * Note that if the tree is modified or destroyed the value pointers might no longer point to valid values. + * The list of values will be sorted by key (as defined by the key compare function, smallest first). + * + * @param [in] tree A pointer to a `PARCTreeRedBlack`. + * @return A list of (pointers to) values. + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCArrayList *parcTreeRedBlack_Values(const PARCTreeRedBlack *tree); + +/** + * Calculate if two `PARCTreeRedBlack`s are equal + * + * Compare 2 `PARCTreeRedBlack` for equality. + * Two trees are equal if they have the same keys associated with the same values. The values + * will be compared using the valueEquals function provided on create. If this function was not + * provided it will compare the actual values. + * + * @param [in] tree1 A pointer to a `PARCTreeRedBlack`. + * @param [in] tree2 A pointer to another `PARCTreeRedBlack`. + * @return true if the trees are equal, zero otherwise + * + * Example: + * @code + * <#example#> + * @endcode + */ +int parcTreeRedBlack_Equals(const PARCTreeRedBlack *tree1, const PARCTreeRedBlack *tree2); + +/** + * Copy a RedBlack Tree + * + * Crete a copy of a RedBlack Tree. + * This will create a completely new tree. It will copy every key and every value using the Copy functions + * provided at tree creation. If these functions are NULL then the numeric values will be copied directly. + * + * @param [in] source_tree A pointer to a `PARCTreeRedBlack` to be copied + * @return NULL Error copying the tree. + * @return Non-NULL A copy of the `PARCTreeRedBlack`. + * + * Example: + * @code + * { + * PARCTreeRedBlack * source_tree = parcTreeRedBlack_Create(.....); + * + * ... operations on tree ... + * + * PARCTreeRedBlack * tree_copy = parcTreeRedBlack_Copy(source_tree); + * + * ... operations on tree ... + * + * parcTreeRedBlack_Destroy(&source_tree); + * parcTreeRedBlack_Destroy(&tree_copy); + * } + * @endcode + */ +PARCTreeRedBlack *parcTreeRedBlack_Copy(const PARCTreeRedBlack *source_tree); +#endif // libparc_parc_TreeRedBlack_h diff --git a/libparc/parc/algol/parc_URI.c b/libparc/parc/algol/parc_URI.c new file mode 100644 index 00000000..172468d8 --- /dev/null +++ b/libparc/parc/algol/parc_URI.c @@ -0,0 +1,468 @@ +/* + * 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 <LongBow/runtime.h> + +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <ctype.h> +#include <stdarg.h> + +#include <parc/algol/parc_URI.h> + +#include <parc/algol/parc_URIPath.h> +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_Buffer.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_SafeMemory.h> +#include <parc/algol/parc_ArrayList.h> +#include <parc/algol/parc_Hash.h> +#include <parc/algol/parc_BufferComposer.h> + +char *sub_delims = "!$&'()*+,;="; +char *gen_delims = ":/?#[]@"; + +#define isSubDelims(c) (c != 0 && strchr(sub_delims, c) != NULL) +#define isGenDelims(c) (c != 0 && strchr(gen_delims, c) != NULL) +#define isDigit(c) (c >= '0' && c <= '9') +#define isAlpha(c) (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') + +#define isUnreserved(c) (isAlpha(c) || isDigit(c) || c == '-' || c == '.' || c == '_' || c == '~') +#define isReserved(c) (isGenDelims(c) || isSubDelims(c)) +#define isPchar(c) (isUnreserved(c) || isSubDelims(c) || c == ':' || c == '@') + +struct parc_uri { + char *scheme; + char *authority; + PARCURIPath *path; + char *query; + char *fragment; +}; + +static void +_parcURI_Finalize(PARCURI **uriPtr) +{ + assertNotNull(uriPtr, "Parameter must be a non-null pointer to a pointer to a PARCURI instance."); + + PARCURI *uri = *uriPtr; + + if (uri->scheme != NULL) { + parcMemory_Deallocate((void **) &uri->scheme); + } + + if (uri->authority != NULL) { + parcMemory_Deallocate((void **) &(uri->authority)); + } + + if (uri->path) { + parcURIPath_Release(&uri->path); + } + + if (uri->query != NULL) { + parcMemory_Deallocate((void **) &(uri->query)); + } + + if (uri->fragment != NULL) { + parcMemory_Deallocate((void **) &(uri->fragment)); + } +} + +parcObject_ExtendPARCObject(PARCURI, _parcURI_Finalize, parcURI_Copy, parcURI_ToString, parcURI_Equals, NULL, NULL, NULL); + +PARCURI * +parcURI_Create(void) +{ + PARCURI *result = parcObject_CreateAndClearInstance(PARCURI); + return result; +} + + +PARCURI * +parcURI_CreateFromValist(const char *restrict format, va_list argList) +{ + PARCURI *result = NULL; + + char *string; + if (vasprintf(&string, format, argList) != -1) { + result = parcURI_Parse(string); + free(string); + } + return result; +} + +PARCURI * +parcURI_CreateFromFormatString(const char *restrict format, ...) +{ + va_list argList; + va_start(argList, format); + + PARCURI *result = parcURI_CreateFromValist(format, argList); + + va_end(argList); + + return result; +} + +parcObject_ImplementAcquire(parcURI, PARCURI); + +parcObject_ImplementRelease(parcURI, PARCURI); + +static bool +_parcURI_SchemeEquals(const char *schemeA, const char *schemeB) +{ + if (schemeA == schemeB) { + return true; + } + if (schemeA == NULL || schemeB == NULL) { + return false; + } + return strcmp(schemeA, schemeB) == 0; +} + +static bool +_parcURI_AuthorityEquals(const char *authorityA, const char *authorityB) +{ + if (authorityA == authorityB) { + return true; + } + if (authorityA == NULL || authorityB == NULL) { + return false; + } + return strcmp(authorityA, authorityB) == 0; +} + +static bool +_parcURI_QueryEquals(const char *queryA, const char *queryB) +{ + if (queryA == queryB) { + return true; + } + if (queryA == NULL || queryB == NULL) { + return false; + } + return strcmp(queryA, queryB) == 0; +} + +static bool +_parcURI_FragmentEquals(const char *fragmentA, const char *fragmentB) +{ + if (fragmentA == fragmentB) { + return true; + } + if (fragmentA == NULL || fragmentB == NULL) { + return false; + } + return strcmp(fragmentA, fragmentB) == 0; +} + +bool +parcURI_Equals(const PARCURI *uriA, const PARCURI *uriB) +{ + if (uriA == uriB) { + return true; + } + if (uriA == NULL || uriB == NULL) { + return false; + } + + if (_parcURI_SchemeEquals(uriA->scheme, uriB->scheme)) { + if (_parcURI_AuthorityEquals(uriA->authority, uriB->authority)) { + if (parcURIPath_Equals(uriA->path, uriB->path)) { + if (_parcURI_QueryEquals(uriA->query, uriB->query)) { + if (_parcURI_FragmentEquals(uriA->fragment, uriB->fragment)) { + return true; + } + } + } + } + } + return false; +} + +/* + * Parse and return a copy of the scheme portion of the URI. + * + * If this function returns successfully, + * the input parameter @p pointer will point to either a null-byte or the first character + * after the ':' separating the scheme from the rest of the URI. + * + * @return non-NULL An allocated string copy of the string which must be freed by the caller via <code>parcMemory_Deallocate</code>. + * @return NULL The scheme is malformed. + */ +static char * +_parseScheme(const char *uri, const char **pointer) +{ + size_t length = 0; + + const char *p = uri; + while (*p != 0 && *p != ':') { + length++; + p++; + } + if (*p == 0) { + return NULL; + } + + if (length == 0) { + return NULL; + } + + char *result = parcMemory_StringDuplicate(uri, length); + + *pointer = (char *) &uri[length + 1]; + return result; +} + +/** + * @function _parseAuthority + * @abstract Parse the authority portion of a URI, if present. + * @discussion + * A URI may have an optional authority component. + * If the given string begins with a double forward slash ("//"), + * then it is followed by an authority part and a path. + * If the string doesn't begin with ("//") it contains only a path and this + * function simply returns NULL and setting the give pointer to the first + * character of the (expected) path. + * + * @param string A pointer to the start of the (potential) authority component. + * @param pointer A pointer to a character pointer that will be assigned point to the first character that begins the path. + * @return An allocated string, to be freed via parcMemory_Deallocate, if the authority portion is present or NULL if otherwise. + */ +static char * +_parseAuthority(const char *string, const char **pointer) +{ + if (string[0] == '/' && string[1] == '/') { + size_t length = 0; + for (const char *p = &string[2]; *p != '/'; p++) { + if (*p == 0) { + *pointer = p; + break; + } + length++; + } + + char *result = parcMemory_StringDuplicate(&string[2], length); + // The pointer must point to the first character *after* the '/' character as the '/' is not part of the path. + *pointer = &(&string[2])[length]; + return result; + } + *pointer = string; + return NULL; +} + +static char * +_parseQuery(const char *string, const char **pointer) +{ + if (*string != '?') { + return NULL; + } + + string++; + size_t length = 0; + for (const char *p = string; *p != 0 && *p != '#'; p++) { + length++; + } + + char *result = parcMemory_StringDuplicate(string, length); + *pointer = &string[length]; + return result; +} + +static char * +_parseFragment(const char *string, const char **pointer) +{ + if (*string != '#') { + return NULL; + } + string++; + size_t length = 0; + for (const char *p = string; *p != 0; p++) { + length++; + } + char *result = parcMemory_StringDuplicate(string, length); + + *pointer = &string[length]; + return result; +} + +static void +_parcURI_SetScheme(PARCURI *uri, const char *scheme) +{ + if (uri->scheme != NULL) { + parcMemory_Deallocate((void **) &(uri->scheme)); + } + if (scheme == NULL) { + uri->scheme = NULL; + } else { + uri->scheme = parcMemory_StringDuplicate(scheme, strlen(scheme)); + } +} + +static void +_parcURI_SetAuthority(PARCURI *uri, const char *authority) +{ + if (uri->authority != NULL) { + parcMemory_Deallocate((void **) &(uri->authority)); + } + if (authority == NULL) { + uri->authority = NULL; + } else { + uri->authority = parcMemory_StringDuplicate(authority, strlen(authority)); + } +} + +static void +_parcURI_SetQuery(PARCURI *uri, const char *query) +{ + if (uri->query != NULL) { + parcMemory_Deallocate((void **) &(uri->query)); + } + if (query == NULL) { + uri->query = NULL; + } else { + uri->query = parcMemory_StringDuplicate(query, strlen(query)); + } +} + +static void +_parcURI_SetFragment(PARCURI *uri, const char *fragment) +{ + if (uri->fragment != NULL) { + parcMemory_Deallocate((void **) &(uri->fragment)); + } + if (fragment == NULL) { + uri->fragment = NULL; + } else { + uri->fragment = parcMemory_StringDuplicate(fragment, strlen(fragment)); + } +} + +PARCURI * +parcURI_Parse(const char *string) +{ + const char *pointer = string; + + PARCURI *result = parcURI_Create(); + + if (result != NULL) { + result->scheme = _parseScheme(pointer, &pointer); + if (result->scheme != NULL) { + result->authority = _parseAuthority(pointer, &pointer); + result->path = parcURIPath_Parse(pointer, &pointer); + result->query = _parseQuery(pointer, &pointer); + result->fragment = _parseFragment(pointer, &pointer); + } else { + parcURI_Release(&result); + result = NULL; + } + } + + return result; +} + +PARCURI * +parcURI_Copy(const PARCURI *uri) +{ + PARCURI *result = parcURI_Create(); + + if (result != NULL) { + _parcURI_SetScheme(result, parcURI_GetScheme(uri)); + _parcURI_SetAuthority(result, parcURI_GetAuthority(uri)); + result->path = parcURIPath_Copy(parcURI_GetPath(uri)); + _parcURI_SetQuery(result, parcURI_GetQuery(uri)); + _parcURI_SetFragment(result, parcURI_GetFragment(uri)); + } + + return result; +} + +PARCBufferComposer * +parcURI_BuildString(const PARCURI *uri, PARCBufferComposer *composer) +{ + parcBufferComposer_PutStrings(composer, parcURI_GetScheme(uri), ":", NULL); + + if (parcURI_GetAuthority(uri)) { + parcBufferComposer_PutString(composer, "//"); + parcBufferComposer_PutString(composer, parcURI_GetAuthority(uri)); + } + + parcBufferComposer_PutString(composer, "/"); + parcURIPath_BuildString(parcURI_GetPath(uri), composer); + + if (parcURI_GetQuery(uri)) { + parcBufferComposer_PutStrings(composer, "?", parcURI_GetQuery(uri), NULL); + } + + if (parcURI_GetFragment(uri)) { + parcBufferComposer_PutStrings(composer, "#", parcURI_GetFragment(uri), NULL); + } + + return composer; +} + +char * +parcURI_ToString(const PARCURI *uri) +{ + char *result = NULL; + + PARCBufferComposer *composer = parcBufferComposer_Create(); + if (composer != NULL) { + if (parcURI_BuildString(uri, composer) != NULL) { + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + } + parcBufferComposer_Release(&composer); + } + + return result; +} + +const char * +parcURI_GetScheme(const PARCURI *uri) +{ + return uri->scheme; +} + +const char * +parcURI_GetAuthority(const PARCURI *uri) +{ + return uri->authority; +} + +PARCURIPath * +parcURI_GetPath(const PARCURI *uri) +{ + return uri->path; +} + +const char * +parcURI_GetQuery(const PARCURI *uri) +{ + return uri->query; +} + +const char * +parcURI_GetFragment(const PARCURI *uri) +{ + return uri->fragment; +} diff --git a/libparc/parc/algol/parc_URI.h b/libparc/parc/algol/parc_URI.h new file mode 100644 index 00000000..7ac6be1f --- /dev/null +++ b/libparc/parc/algol/parc_URI.h @@ -0,0 +1,412 @@ +/* + * 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. + */ + +/** + * @file parc_URI.h + * @ingroup networking + * @brief A Universal Resource Identifier + * + * An RF2396 compliant URI implementation with facilities for composition, decomposition, + * comparison, manipulation, etc. + * + */ +#ifndef libparc_parc_URI_h +#define libparc_parc_URI_h + +#include <parc/algol/parc_URIPath.h> +#include <parc/algol/parc_BufferComposer.h> + +struct parc_uri; +typedef struct parc_uri PARCURI; + +/** + * Create a new `PARCURI` object. + * + * The new `PARCURI` object is empty. + * + * @return A pointer to a `PARCURI` instance. + * + * Example: + * @code + * { + * PARCURI *uri = parcURI_Create(); + * ... + * parcURI_Release(&uri); + * } + * @endcode + */ +PARCURI *parcURI_Create(void); + +/** + * Create a new instance of `PARCURI` from the given format string and arguments. + * + * @param [in] format A pointer to a nul-terminated printf format string + * @param [in] argList A pointer to a valid `va_list` + * + * @return non-NULL A pointer to a valid PARCURI instance. + * @return NULL An error occurred. + * + * Example: + * @code + * { + * char * + * createFormatString(const char * restrict format, ...) + * { + * va_list argList; + * va_start(argList, format); + * + * PARCURI *uri = parcURI_CreateFromValist(format, argList); + * + * va_end(argList); + * return uri; + * } + * } + * @endcode + */ +PARCURI *parcURI_CreateFromValist(const char *restrict format, va_list argList); + +/** + * Create a new instance of `PARCURI` from the given format string and variable number of parameters. + * + * @param [in] format A pointer to a nul-terminated printf format string + * @param [in] ... A variable number of parameters. + * + * @return non-NULL A pointer to a valid PARCURI instance. + * @return NULL An error occurred. + * + * Example: + * @code + * { + * PARCURI *uri = parcURI_CreateFromFormatString("http://%s/index.html", "127.0.0.1"); + * } + * @endcode + */ +PARCURI *parcURI_CreateFromFormatString(const char *restrict format, ...); + +/** + * Increase the number of references to a `PARCURI` instance. + * + * Note that new `PARCURI` is not created, + * only that the given `PARCURI` reference count is incremented. + * Discard the reference by invoking {@link parcURI_Release}. + * + * @param uri A pointer to the original instance. + * @return The value of the input parameter @p instance. + * + * Example: + * @code + * { + * PARCURI *x = parcURI_Create(); + * + * PARCURI *x2 = parcURI_Acquire(x); + * + * parcURI_Release(&x); + * parcURI_Release(&x2); + * } + * @endcode + * + * @see parcURI_Release + */ +PARCURI *parcURI_Acquire(const PARCURI *uri); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] uriPtr A pointer to a pointer to the instance to release. + * + * + * Example: + * @code + * { + * PARCURI *x = parcURI_Create(); + * + * parcURI_Release(&x); + * } + * @endcode + */ +void parcURI_Release(PARCURI **uriPtr); + +/** + * Parse a well-formed URI into a `PARCURI` instance. + * + * The URI string must be null-terminated so that parsing can detect the + * end of the path correctly. + * + * @param [in] string The URI C-string used to create the `PARCURI` instance. + * + * @return non-NULL A pointer to an allocated `PARCURI` structure which must be destroyed via {@link parcURI_Release()}. + * @return NULL The URI was malformed. + * + * Example: + * @code + * { + * const char *uriString = "http://parc.com"; + * + * PARCURI *uri = parcURI_Parse(uriString); + * if (uri == NULL) { + * printf("Malformed URI '%s', uriString); + * } + * } + * @endcode + */ +PARCURI *parcURI_Parse(const char *string); + +/** + * Create a deep copy of the given `PARCURI` instance. + * + * @param [in] uri A `PARCURI` pointer. + * + * @return A deep copy of the given `PARCURI` instance. + * + * Example: + * @code + * { + * const char *uriString = "http://parc.com"; + * PARCURI *uri = parcURI_Parse(uriString); + * + * PARCURI *copy = parcURI_Copy(uri); + * // uri and copy will contain equivalent paths + * + * parcURI_Release(&uri); + * parcURI_Release(©); + * } + * @endcode + */ +PARCURI *parcURI_Copy(const PARCURI *uri); + +/** + * Return true if two `PARCURI` instances are equal. + * + * The following equivalence relations on non-null `PARCURI` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, equals(x, x) must return true. + * + * * It is symmetric: for any non-null reference values x and y, equals(x, y) must return true if and only if + * equals(y x) returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * equals(x, y) returns true and + * equals(y, z) returns true, + * then equals(x, z) must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of equals(x, y) + * consistently return true or consistently return false. + * + * * For any non-null reference value x, equals(x, NULL)) must return false. + * + * @param [in] uriA The first `PARCURI` instance. + * @param [in] uriB The second `PARCURI` instance. + * + * @return true the given `PARCURI` instances are equal. + * @return false the given `PARCURI` instances are not equal. + * + * Example: + * @code + * { + * const char *uriString = "http://parc.com"; + * PARCURI *uri = parcURI_Parse(uriString); + * PARCURI *copy = parcURI_Copy(uri); + * + * if (parcURI_Equals(uri, copy)) { + * printf("Paths are equal.\n"); + * } else { + * printf("Paths are NOT equal.\n"); + * } + * + * parcURI_Release(&uri); + * parcURI_Release(©); + * } + * @endcode + */ +bool parcURI_Equals(const PARCURI *uriA, const PARCURI *uriB); + +/** + * Get the scheme part of the given URI. + * + * The scheme of a URI path is the "type", e.g., labeled content + * identifier "lci" for "lci:/foo/bar". + * + * @param [in] uri A `PARCURI` pointer. + * + * @return char* The scheme of the given `PARCURI` instance + * @return NULL If no scheme is available. + * + * Example: + * @code + * { + * const char *uriString = "http://parc.com"; + * PARCURI *uri = parcURI_Parse(uriString); + * + * const char *scheme = parcURI_GetScheme(uri); // will be "lci" + * + * parcURI_Release(&uri); + * } + * @endcode + */ +const char *parcURI_GetScheme(const PARCURI *uri); + +/** + * Get the authority part of the given `PARCURI`. + * + * The authority part of a URI is the string: username at host:port + * + * @param [in] uri A `PARCURI` pointer. + * + * @return char* The authority of the given `PARCURI` instance + * @return NULL If no scheme is available. + * + * Example: + * @code + * { + * const char *uriString = "http://chris@parc.com:80"; + * PARCURI *uri = parcURI_Parse(uriString); + * + * const char *authority = parcURI_GetAuthority(uri); // will be "chris@parc.com:80" + * + * parcURI_Release(&uri); + * } + * @endcode + */ +const char *parcURI_GetAuthority(const PARCURI *uri); + +/** + * Get the {@link PARCURIPath} part of the given URI. + * + * Every `PARCURI` contains a `PARCURIPath` consisting of the path portion of the URI. + * + * @param [in] uri A `PARCURI` pointer. + * + * @return The `PARCURIPath` part of the given URI. + * + * Example: + * @code + * { + * const char *uriString = "http://parc.com/foo/bar/"; + * PARCURI *uri = parcURI_Parse(uriString); + * + * const char *path = parcURI_GetPath(uri); // will be "/foo/bar/" + * + * parcURI_Release(&uri); + * } + * @endcode + */ +PARCURIPath *parcURI_GetPath(const PARCURI *uri); + +/** + * Get the query part of the given PARCURI`. + * + * Queries are contiguous key-value string segments of a URI. + * For example, the query of URI string "http://parc.com/x=1&y=2&z=3" is "x=1&y=2&z=3" + * + * @param [in] uri A `PARCURI` pointer. + * + * @return char* The query string, if present, for the given URI + * @return NULL If no query string is present + * + * Example: + * @code + * { + * const char *uriString = "http://parc.com/x=1&y=2&z=3"; + * PARCURI *uri = parcURI_Parse(uriString); + * + * const char *path = parcURI_GetQuery(uri); // will be "x=1&y=2&z=3" + * + * parcURI_Release(&uri); + * } + * @endcode + */ +const char *parcURI_GetQuery(const PARCURI *uri); + +/** + * Get the fragment part of the given `PARCURI`. + * + * The fragment of a URI is the string component following the '#' character. + * For example, the fragment of URI string "http://parc.com/index.html#info" is "info" + * + * @param [in] uri A `PARCURI` pointer. + * + * @return char* The fragment of the URI, if present + * @return NULL If no fragment is in the URI + * + * Example: + * @code + * { + * const char *uriString = "http://parc.com/index.html#info"; + * PARCURI *uri = parcURI_Parse(uriString); + * + * const char *path = parcURI_GetFragment(uri); // will be "info" + * + * parcURI_Release(&uri); + * } + * @endcode + */ +const char *parcURI_GetFragment(const PARCURI *uri); + +/** + * Append a representation of the specified `PARCURI` instance to the given `PARCBufferComposer`. + * + * @param [in] uri A pointer to a `PARCURI` instance whose representation should be appended to the @p composer. + * @param [in,out] composer A pointer to a `PARCBufferComposer` instance to be modified. + * + * @return NULL Cannot allocate memory. + * @return non-NULL The @p composer. + * + * Example: + * @code + * { + * PARCBufferComposer *result = parcBufferComposer_Create(); + * + * parcURI_BuildString(instance, result); + * + * char *string = parcBufferComposer_ToString(result); + * printf("Hello: %s\n", string); + * parcMemory_Deallocate(string); + * + * parcBufferComposer_Release(&result); + * } + * @endcode + */ +PARCBufferComposer *parcURI_BuildString(const PARCURI *uri, PARCBufferComposer *composer); + +/** + * Produce an allocated null-terminated string representation of the given URI. + * + * The returned value must be destroyed via `parcMemory_Deallocate` + * + * @param [in] uri The `PARCURI` instance to format. + * + * @return An allocated null-terminated string representation of the given URI. + * + * Example: + * @code + * { + * const char *uriString = "http://parc.com"; + * PARCURI *uri = parcURI_Parse(uriString); + * + * printf("path string = %s\n", parcURI_ToString(uri)); + * + * parcURI_Release(&uri); + * } + * @endcode + */ +char *parcURI_ToString(const PARCURI *uri); +#endif // libparc_parc_URI_h diff --git a/libparc/parc/algol/parc_URIAuthority.c b/libparc/parc/algol/parc_URIAuthority.c new file mode 100644 index 00000000..ac2ee351 --- /dev/null +++ b/libparc/parc/algol/parc_URIAuthority.c @@ -0,0 +1,133 @@ +/* + * 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 <LongBow/runtime.h> + +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <ctype.h> +#include <stdarg.h> + +#include <parc/algol/parc_URIAuthority.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_SafeMemory.h> + +struct parc_uri_authority { + char *userinfo; + char *hostName; + long port; +}; + +static void +_parcURIAuthority_Finalize(PARCURIAuthority **authorityPtr) +{ + PARCURIAuthority *authority = *authorityPtr; + parcMemory_SafeFree(authority->userinfo); + parcMemory_SafeFree(authority->hostName); +} + +parcObject_ExtendPARCObject(PARCURIAuthority, _parcURIAuthority_Finalize, NULL, NULL, parcURIAuthority_Equals, NULL, NULL, NULL); + +PARCURIAuthority * +parcURIAuthority_Create(void) +{ + PARCURIAuthority *result = parcObject_CreateInstance(PARCURIAuthority); + result->userinfo = 0; + result->hostName = 0; + result->port = 0; + return result; +} + +parcObject_ImplementAcquire(parcURIAuthority, PARCURIAuthority); + +parcObject_ImplementRelease(parcURIAuthority, PARCURIAuthority); + +PARCURIAuthority * +parcURIAuthority_Parse(const char *authority) +{ + PARCURIAuthority *result = parcURIAuthority_Create(); + + char *atSign = strchr(authority, '@'); + if (atSign != NULL) { + result->userinfo = parcMemory_StringDuplicate(authority, atSign - authority); + authority = ++atSign; + } + // Support literal IPv6 address specifications (i.e. [::0]:80) + char *rightBracket = strrchr(authority, ']'); + char *lastColon = strrchr(authority, ':'); + if (rightBracket != NULL) { + result->hostName = parcMemory_StringDuplicate(authority, rightBracket - authority + 1); + if ((lastColon - rightBracket) > 0) { + result->port = (short) strtol(++lastColon, NULL, 10); + } + } else if (lastColon != NULL) { + result->hostName = parcMemory_StringDuplicate(authority, lastColon - authority); + result->port = (short) strtol(++lastColon, NULL, 10); + } else { + result->hostName = parcMemory_StringDuplicate(authority, strlen(authority)); + } + + return result; +} + +const char * +parcURIAuthority_GetUserInfo(const PARCURIAuthority *authority) +{ + return authority->userinfo; +} + +const char * +parcURIAuthority_GetHostName(const PARCURIAuthority *authority) +{ + return authority->hostName; +} + +long +parcURIAuthority_GetPort(const PARCURIAuthority *authority) +{ + return authority->port; +} + +bool +parcURIAuthority_Equals(const PARCURIAuthority *authA, const PARCURIAuthority *authB) +{ + if (authA == authB) { + return true; + } + if (authA == NULL || authB == NULL) { + return false; + } + + if (strcmp(authA->hostName, authB->hostName) == 0) { + if (strcmp(authA->userinfo, authB->userinfo) == 0) { + if (authA->port == authB->port) { + return true; + } + return false; + } + return false; + } + + return false; +} diff --git a/libparc/parc/algol/parc_URIAuthority.h b/libparc/parc/algol/parc_URIAuthority.h new file mode 100644 index 00000000..49d19734 --- /dev/null +++ b/libparc/parc/algol/parc_URIAuthority.h @@ -0,0 +1,237 @@ +/* + * 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. + */ + +/** + * @file parc_URIAuthority.h + * @ingroup networking + * @brief A Universal Resource Identifier (URI) Authority + * + */ +#ifndef libparc_parc_URIAuthority_h +#define libparc_parc_URIAuthority_h + +#include <stdbool.h> + +struct parc_uri_authority; +typedef struct parc_uri_authority PARCURIAuthority; + +/** + * Create a new `PARCURIAuthority` object. + * + * The new `PARCURIAuthority` object is empty. + * + * @return A pointer to a `PARCURIAuthority` instance. + * + * Example: + * @code + * { + * PARCURIAuthority *auth = parcURIAuthority_Create(); + * ... + * parcURIAuthority_Release(&auth); + * } + * @endcode + */ +PARCURIAuthority *parcURIAuthority_Create(void); + +/** + * Increase the number of references to a `PARCURIAuthority` instance. + * + * Note that new `PARCURIAuthority` is not created, + * only that the given `PARCURIAuthority` reference count is incremented. + * Discard the reference by invoking `parcURIAuthority_Release`. + * + * @param auth A pointer to the original instance. + * @return The value of the input parameter @p instance. + * + * Example: + * @code + * { + * PARCURIAuthority *x = parcURIAuthority_Create(); + * + * PARCURIAuthority *x2 = parcURIAuthority_Acquire(x); + * + * parcURIAuthority_Release(&x); + * parcURIAuthority_Release(&x2); + * } + * @endcode + * + * @see parcURIAuthority_Release + */ +PARCURIAuthority *parcURIAuthority_Acquire(const PARCURIAuthority *auth); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] authPtr A pointer to a pointer to the instance to release. + * + * Example: + * @code + * { + * PARCURIAuthority *auth = parcURIAuthority_Create(); + * + * parcURIAuthority_Release(&auth); + * } + * @endcode + */ +void parcURIAuthority_Release(PARCURIAuthority **authPtr); + +/** + * Return true if two `PARCURIAuthority` instances are equal. + * + * The following equivalence relations on non-null `PARCURIAuthority` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, parcURIAuthority_Equals(x, x) must return true. + * + * * It is symmetric: for any non-null reference values x and y, parcURIAuthority_Equals(x, y) must return true if and only if + * parcURIAuthority_Equals(y x) returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * parcURIAuthority_Equals(x, y) returns true and + * parcURIAuthority_Equals(y, z) returns true, + * then parcURIAuthority_Equals(x, z) must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of parcURIAuthority_Equals(x, y) + * consistently return true or consistently return false. + * + * * For any non-null reference value x, parcURIAuthority_Equals(x, NULL)) must return false. + * + * @param [in] authA The first `PARCURIAuthority` instance. + * @param [in] authB The second `PARCURIAuthority` instance. + * + * @return true the given `PARCURIAuthority` instances are equal. + * @return false the given `PARCURIAuthority` instances are not equal. + * + * Example: + * @code + * { + * const char *uriString = "http://chris@parc.com:80"; + * PARCURI *uri = parcURI_Parse(uriString); + * PARCURI_Authority *authority = parcURIAuthority_Parse(parcURI_GetAuthority(uri)); + * PARCURI_Authority *handle = parcURIAuthority_Acquire(authority); + * + * if (parcURIAuthority_Equals(authority, handle)) { + * printf("Authorities are equal.\n"); + * } else { + * printf("Authorities are NOT equal.\n"); + * } + * + * parcURIAuthority_Release(&uri); + * parcURIAuthority_Release(©); + * } + * @endcode + */ +bool parcURIAuthority_Equals(const PARCURIAuthority *authA, const PARCURIAuthority *authB); + +/** + * Produce a `PARCURI_Authority` type from the authority string. + * + * The returned value must be destroyed via {@link parcMemory_Deallocate}. + * + * @param [in] authority The authority string to parse. + * + * @return A newly allocated `PARCURI_Authority` string. + * + * Example: + * @code + * { + * const char *uriString = "http://chris@parc.com:80"; + * PARCURI *uri = parcURI_Parse(uriString); + * + * PARCURI_Authority *authority = parcURIAuthority_Parse(parcURI_GetAuthority(uri)); + * // use the authority + * + * parcURIAuthority_Release(&authority); + * parcURI_Release(&uri); + * } + * @endcode + */ +PARCURIAuthority *parcURIAuthority_Parse(const char *authority); + +/** + * Retrieve the user information from the given `PARCURI_Authority` instance. + * + * @param [in] authority The `PARCURI_Authority` instance being queried. + * + * @return The user info string component of the `PARCURI_Authority` instance. Copy the string prior to modification. + * + * Example: + * @code + * { + * const char *uriString = "http://chris@parc.com:80"; + * PARCURI *uri = parcURI_Parse(uriString); + * + * PARCURI_Authority *authority = parcURIAuthority_Parse(parcURI_GetAuthority(uri)); + * printf("User info: "%s\n", parcURIAuthority_GetUserInfo(authority)); + * + * parcURIAuthority_Release(&authority); + * parcURI_Release(&uri); + * } + * @endcode + */ +const char *parcURIAuthority_GetUserInfo(const PARCURIAuthority *authority); + +/** + * Retrieve the host name from the given `PARCURI_Authority` instance. + * + * @param [in] authority The `PARCURI_Authority` instance being queried. + * + * @return The host name string component of the `PARCURI_Authority` instance. Copy the string prior to modification. + * + * Example: + * @code + * { + * const char *uriString = "http://chris@parc.com:80"; + * PARCURI *uri = parcURI_Parse(uriString); + * + * PARCURI_Authority *authority = parcURIAuthority_Parse(parcURI_GetAuthority(uri)); + * printf("Host name: "%s\n", parcURIAuthority_GetHostName(authority)); + * + * parcURIAuthority_Release(&authority); + * parcURI_Release(&uri); + * } + * @endcode + */ +const char *parcURIAuthority_GetHostName(const PARCURIAuthority *authority); + +/** + * Retrieve the host port from the given `PARCURI_Authority` instance. + * + * @param [in] authority The `PARCURI_Authority` instance being queried. + * + * @return The host port string component of the `PARCURI_Authority` instance. Copy the string prior to modification. + * + * Example: + * @code + * { + * const char *uriString = "http://chris@parc.com:80"; + * PARCURI *uri = parcURI_Parse(uriString); + * + * PARCURI_Authority *authority = parcURIAuthority_Parse(parcURI_GetAuthority(uri)); + * printf("Host port: "%s\n", parcURIAuthority_GetPort(authority)); + * + * parcURIAuthority_Release(&authority); + * parcURI_Release(&uri); + * } + * @endcode + */ +long parcURIAuthority_GetPort(const PARCURIAuthority *authority); +#endif /* defined(libparc_parc_URIAuthority_h) */ diff --git a/libparc/parc/algol/parc_URIPath.c b/libparc/parc/algol/parc_URIPath.c new file mode 100755 index 00000000..ef46dac9 --- /dev/null +++ b/libparc/parc/algol/parc_URIPath.c @@ -0,0 +1,309 @@ +/* + * 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 <LongBow/runtime.h> + +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <ctype.h> +#include <stdarg.h> + +#include <parc/algol/parc_URIPath.h> + +#include <parc/algol/parc_URISegment.h> +#include <parc/algol/parc_ArrayList.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> + +struct parc_uri_path { + PARCArrayList *segments; +}; + +static void +_parcURIPath_Finalize(PARCURIPath **pathPtr) +{ + assertNotNull(pathPtr, "Parameter must be a non-null pointer to a pointer to a PARCURIPath instance."); + + PARCURIPath *path = *pathPtr; + if (path != NULL) { + parcArrayList_Destroy(&path->segments); + } +} + +parcObject_ExtendPARCObject(PARCURIPath, _parcURIPath_Finalize, parcURIPath_Copy, parcURIPath_ToString, parcURIPath_Equals, + parcURIPath_Compare, NULL, NULL); + +PARCURIPath * +parcURIPath_Create(void) +{ + PARCURIPath *result = parcObject_CreateInstance(PARCURIPath); + return result; +} + +parcObject_ImplementAcquire(parcURIPath, PARCURIPath); + +parcObject_ImplementRelease(parcURIPath, PARCURIPath); + +PARCURIPath * +parcURIPath_Append(PARCURIPath *path, const PARCURISegment *segment) +{ + parcArrayList_Add(path->segments, segment); + return path; +} + +PARCURIPath * +parcURIPath_Trim(PARCURIPath *path, size_t numberToRemove) +{ + size_t length = parcArrayList_Size(path->segments); + if (numberToRemove <= length) { + while (numberToRemove--) { + parcArrayList_RemoveAndDestroyAtIndex(path->segments, length - 1); + length--; + } + } + return path; +} + +//PARCURIPath * +//parcURIPath_Parse(const char *string, const char **pointer) +//{ +// PARCURIPath *result = parcURIPath_Create(); +// +// result->segments = parcArrayList_Create((void (*)(void **))parcURISegment_Release); +// if (*string != 0) { +// assertTrue(*string == '/', "Expected initial '/' character."); +// *pointer = string; +// while (**pointer != 0 && **pointer != '?' && **pointer != '#') { +// PARCURISegment *segment = parcURISegment_Parse(++(*pointer), pointer); +// parcURIPath_Append(result, segment); +// } +// } +// +// return result; +//} + +PARCURIPath * +parcURIPath_Parse(const char *string, const char **pointer) +{ + PARCURIPath *result = parcURIPath_Create(); + result->segments = parcArrayList_Create((void (*)(void **))parcURISegment_Release); + + if (*string != 0) { + if (*string != '/') { + parcURIPath_Release(&result); + return NULL; + } + + *pointer = string; + while (**pointer != 0 && **pointer != '?' && **pointer != '#') { + PARCURISegment *segment = parcURISegment_Parse(++(*pointer), pointer); + parcURIPath_Append(result, segment); + } + } + + return result; +} + +bool +parcURIPath_Equals(const PARCURIPath *pathA, const PARCURIPath *pathB) +{ + if (pathA == pathB) { + return true; + } + if (pathA == NULL || pathB == NULL) { + return false; + } + + if (parcArrayList_Size(pathA->segments) == parcArrayList_Size(pathB->segments)) { + for (size_t i = 0; i < parcArrayList_Size(pathA->segments); i++) { + PARCURISegment *segmentA = parcArrayList_Get(pathA->segments, i); + PARCURISegment *segmentB = parcArrayList_Get(pathB->segments, i); + if (!parcURISegment_Equals(segmentA, segmentB)) { + return false; + } + } + return true; + } + return false; +} + +PARCURIPath * +parcURIPath_Copy(const PARCURIPath *path) +{ + assertNotNull(path, "Parameter must be a non-null PARC_URIPath pointer."); + + PARCURIPath *result = parcURIPath_Create(); + result->segments = parcArrayList_Create((void (*)(void **))parcURISegment_Release); + + size_t nSegments = parcURIPath_Count(path); + + for (size_t i = 0; i < nSegments; i++) { + PARCURISegment *segment = parcURIPath_Get(path, i); + PARCURISegment *segmentCopy = parcURISegment_Clone(segment); + parcURIPath_Append(result, segmentCopy); + } + + return result; +} + +PARCURIPath * +parcURIPath_ComposeValist(const PARCURIPath *basePath, va_list varargs) +{ + PARCURIPath *result = parcURIPath_Copy(basePath); + + for (PARCURISegment *segment = va_arg(varargs, PARCURISegment *); segment != NULL; segment = va_arg(varargs, PARCURISegment *)) { + parcURIPath_Append(result, parcURISegment_Clone(segment)); + } + + return result; +} + +PARCURIPath * +parcURIPath_Compose(const PARCURIPath *basePath, ...) +{ + va_list arglist; + va_start(arglist, basePath); + + PARCURIPath *result = parcURIPath_ComposeValist(basePath, arglist); + va_end(arglist); + + return result; +} + +bool +parcURIPath_StartsWith(const PARCURIPath *base, const PARCURIPath *prefix) +{ + size_t prefixSegmentCount = parcURIPath_Count(prefix); + size_t baseSegmentCount = parcURIPath_Count(base); + + if (baseSegmentCount < prefixSegmentCount) { + return false; + } + + for (size_t i = 0; i < prefixSegmentCount; i++) { + PARCURISegment *baseSegment = parcURIPath_Get(base, i); + PARCURISegment *prefixSegment = parcURIPath_Get(prefix, i); + if (parcURISegment_Compare(baseSegment, prefixSegment) != 0) { + return false; + } + } + + return true; +} + +int +parcURIPath_Compare(const PARCURIPath *pathA, const PARCURIPath *pathB) +{ + if (pathA == NULL) { + if (pathB == NULL) { + return 0; + } + return -1; + } else { + if (pathB == NULL) { + return +1; + } + } + + ssize_t countDifference = parcURIPath_Count(pathA) - parcURIPath_Count(pathB); + + if (countDifference != 0) { + return (countDifference > 0 ? 1 : (countDifference < 0) ? -1 : 0); + } + + size_t nSegments = parcURIPath_Count(pathA); + + for (size_t i = 0; i < nSegments; i++) { + PARCURISegment *segmentA = parcURIPath_Get(pathA, i); + PARCURISegment *segmentB = parcURIPath_Get(pathB, i); + int comparison = parcURISegment_Compare(segmentA, segmentB); + if (comparison != 0) { + return comparison; + } + } + return 0; +} + +PARCURISegment * +parcURIPath_Get(const PARCURIPath *path, size_t index) +{ + return (PARCURISegment *) parcArrayList_Get(path->segments, index); +} + +size_t +parcURIPath_Count(const PARCURIPath *path) +{ + size_t nSegments = parcArrayList_Size(path->segments); + return nSegments; +} + +size_t +parcURIPath_Length(const PARCURIPath *path) +{ + size_t result = 0; + + size_t nSegments = parcURIPath_Count(path); + + for (size_t i = 0; i < nSegments; i++) { + PARCURISegment *segment = parcURIPath_Get(path, i); + result += parcURISegment_Length(segment); + if (i < (nSegments - 1)) { + result++; // Include the size of the '/' separators. + } + } + + return result; +} + +PARCBufferComposer * +parcURIPath_BuildString(const PARCURIPath *path, PARCBufferComposer *composer) +{ + size_t nSegments = parcArrayList_Size(path->segments); + + for (size_t i = 0; i < nSegments && composer != NULL; i++) { + if (parcURISegment_BuildString(parcURIPath_Get(path, i), composer) == NULL) { + composer = NULL; + } + if (i < (nSegments - 1)) { + composer = parcBufferComposer_PutChar(composer, '/'); + } + } + + return composer; +} + +char * +parcURIPath_ToString(const PARCURIPath *path) +{ + char *result = NULL; + + PARCBufferComposer *composer = parcBufferComposer_Create(); + if (composer != NULL) { + if (parcURIPath_BuildString(path, composer) != NULL) { + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + } + parcBufferComposer_Release(&composer); + } + + return result; +} diff --git a/libparc/parc/algol/parc_URIPath.h b/libparc/parc/algol/parc_URIPath.h new file mode 100755 index 00000000..d43d8651 --- /dev/null +++ b/libparc/parc/algol/parc_URIPath.h @@ -0,0 +1,494 @@ +/* + * 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. + */ + +/** + * @file parc_URIPath.h + * @ingroup networking + * @brief A Universal Resource Identifier (URI) Path + * + */ +#ifndef libparc_PARCURIPath_h +#define libparc_PARCURIPath_h + +#include <stdlib.h> +#include <stdarg.h> + +#include <parc/algol/parc_URISegment.h> + +struct parc_uri_path; +typedef struct parc_uri_path PARCURIPath; + +/** + * Create a new `PARCURIPath` object. + * + * The new `PARCURIPath` object is empty. + * + * @return A pointer to a `PARCURIPath` instance. + * + * Example: + * @code + * { + * PARCURIPath *path = parcURIPath_Create(); + * ... + * parcURIPath_Release(&path); + * } + * @endcode + */ +PARCURIPath *parcURIPath_Create(void); + +/** + * Increase the number of references to a `PARCURIPath` instance. + * + * Note that new `PARCURIPath` is not created, + * only that the given `PARCURIPath` reference count is incremented. + * Discard the reference by invoking {@link parcURIPath_Release}. + * + * @param auth A pointer to the original instance. + * @return The value of the input parameter @p instance. + * + * Example: + * @code + * { + * PARCURIPath *x = parcURIPath_Create(); + * + * PARCURIPath *x2 = parcURIPath_Acquire(x); + * + * parcURIPath_Release(&x); + * parcURIPath_Release(&x2); + * } + * @endcode + * + * @see parcURIPath_Release + */ +PARCURIPath *parcURIPath_Acquire(const PARCURIPath *auth); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] pathPtr A pointer to a pointer to the instance to release. + * + * Example: + * @code + * { + * PARCURIPath *path = parcURIPath_Create(); + * + * parcURIPath_Release(&auth); + * } + * @endcode + */ +void parcURIPath_Release(PARCURIPath **pathPtr); + +/** + * Parse a complete URI path composed of URI segments. + * + * The parsed path is expected to conform to the syntax of '/' segment ['/' segment] + * terminated by either a null, '?', or '#' character. + * + * @param [in] string A pointer to character array containing the first '/' of the path. + * @param [in] pointer A pointer to character pointer that will point to the first character that is not the path. + * + * @return A newly allocated `PARCURIPath` instance that must be freed via {@link parcURIPath_Release()} + * + * Example: + * @code + * { + * char *pointer; + * PARCURIPath *path = parcURIPath_Parse("lci:/foo/bar/, &pointer); + * // use the complete URI path + * parcURIPath_Release(&path); + * } + * @endcode + */ +PARCURIPath *parcURIPath_Parse(const char *string, const char **pointer); + +/** + * Compares two `PARCURIPath` instances for order. + * + * As strings, URI paths are compared in normal lexographical order. This + * is analogous to strcmp(...). + * + * @param [in] pathA A `PARCURIPath` pointer, or NULL. + * @param [in] pathB A `PARCURIPath` pointer, or NULL. + * + * @return A negative integer, zero, or a positive integer as a is less than, equal to, or greater than b, accordingly. + * + * Example: + * @code + * { + * char *pointer; + * PARCURIPath *pathA = parcURIPath_Parse("lci:/foo/bar/, &pointer); + * PARCURIPath *pathB = parcURIPath_Parse("lci:/foo/bar/, &pointer); + * int cmp = parcURIPath_Compare(pathA, pathB); + * // cmp will be zero since both paths are the same + * } + * @endcode + */ +int parcURIPath_Compare(const PARCURIPath *pathA, const PARCURIPath *pathB); + +/** + * Create a new `PARCURIPath` comprised of a basePath concatenated with zero or more `PARCURISegment` instances. + * + * Create a new `PARCURIPath` instance comprised of the given `PARCURIPath` + * concatenated with the null terminated list of {@link PARCURISegment} instances. + * + * @param [in] basePath The base prefix path used to compose a new path + * @param [in] ... Any additional `PARCURISegment` instances that will be appended to the URI path. + * + * @return A newly allocated `PARCURIPath` instance + * + * Example: + * @code + * { + * char *pointer; + * PARCURIPath *basePath = parcURIPath_Parse("lci:/foo/, &pointer); + * PARCURISegment *segment = parcURISegment_Create(3, "bar"); + * PARCURIPath *path = parcURIPath_Compose(basePath, segment); + * + * // use the new composed path as needed + * + * parcURIPath_Release(&path); + * parcURIPath_Release(&basePath); + * parcURISegment_Destroy(&segment); + * } + * @endcode + */ +PARCURIPath *parcURIPath_Compose(const PARCURIPath *basePath, ...); + +/** + * Create a new `PARCURIPath` comprised of a basePath concatenated with the number of given `PARCURISegment` + * instances provided by @p varargs. + * + * The variable number of `PARCURISegment` instances is signaled by the value `NULL` as the last element in @p varargs. + * + * @param [in] basePath The base prefix path used to compose a new path + * @param [in] varargs A valid va_list. + * + * @return A newly allocated `PARCURIPath` instance + * + * Example: + * @code + * PARCURIPath * + * myFunction(const PARCURIPath *basePath, ...) + * { + * va_list arglist; + * va_start(arglist, basePath); + * + * PARCURIPath *result = parcURIPath_ComposeValist(basePath, arglist); + * va_end(arglist); + * + * return result; + * } + * @endcode + */ +PARCURIPath *parcURIPath_ComposeValist(const PARCURIPath *basePath, va_list varargs); + +/** + * Concatenate two paths together to form a single path. + * + * Concatenating "/a/b/" and "/c/d" URI paths will yield: "/a/b/c/d/". + * + * @param [in] pathA Pointer to the first path (prefix) + * @param [in] pathB Pointer to the second path (suffix) + * + * @return A `PARCURIPath` instance containing the concatenation of pathA and pathB, must be freed via {@link parcURIPath_Release()} + * + * Example: + * @code + * { + * char *pointer; + * PARCURIPath *prefix = parcURIPath_Parse("lci:/foo/, &pointer); + * PARCURIPath *suffix = parcURIPath_Parse("/bar/", &pointer); + * PARCURIPath *concat = parcURIPath_Concat(prefix, suffix); + * + * // use the new path as needed + * + * parcURIPath_Release(&prefix); + * parcURIPath_Release(&suffix); + * parcURIPath_Release(&concat); + * } + * @endcode + */ +PARCURIPath *parcURIPath_Concat(PARCURIPath *pathA, PARCURIPath *pathB); + +/** + * Determine if two `PARCURIPath` instances are equal. + * + * This function implements the following equivalence relations on non-null `PARCURIPath` instances: + * + * * It is reflexive: for any non-null reference value x, `parcURIPath_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `parcURIPath_Equals(x, y)` must return true if and only if + * `parcURIPath_Equals(y x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcURIPath_Equals(x, y)` returns true and + * `parcURIPath_Equals(y, z)` returns true, + * then `parcURIPath_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `parcURIPath_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `parcURIPath_Equals(x, NULL)` must return false. + * + * + * @param [in] pathA First instance + * @param [in] pathB Second instance + * + * @return true Equal `PARCURIPath` instances + * @return false Otherwise + * + * Example: + * @code + * { + * char *pointer; + * PARCURIPath *pathA = parcURIPath_Parse("lci:/foo/, &pointer); + * PARCURIPath *pathB = parcURIPath_Parse("lci:/foo/, &pointer); + * + * if (parcURIPath_Equals(pathA, pathB) { + * printf("Paths are equal\n"); + * } + * + * parcURIPath_Release(&pathA); + * parcURIPath_Release(&pathB); + * } + * @endcode + */ +bool parcURIPath_Equals(const PARCURIPath *pathA, const PARCURIPath *pathB); + +/** + * Create a copy of the given `PARCURIPath`. + * + * This is a deep copy of the instance. + * + * @param [in] path The path to copy. + * + * @return A copy of the given `PARCURIPath`. + * + * Example: + * @code + * { + * char *pointer; + * PARCURIPath *path = parcURIPath_Parse("lci:/foo/, &pointer); + * PARCURIPath *copy = parcURIPath_Copy(path); + * + * // use the copy as needed + * + * parcURIPath_Release(&path); + * parcURIPath_Release(©); + * } + * @endcode + */ +PARCURIPath *parcURIPath_Copy(const PARCURIPath *path); + +/** + * Append a path segment to an existing URI path. + * + * Appending "bar" to "lci:/foo" will yield "lci:/foo/bar". + * This modifies the URI path instance in place - it does not allocate + * a new instance. + * + * @param [in,out] path The `PARCURIPath` instance to which the segment is appended + * @param [in] segment The {@link PARCURISegment} to append to the path + * + * @return The modified `PARCURIPath` instance (equal to the first parameter). + * + * Example: + * @code + * { + * char *pointer; + * PARCURIPath *path = parcURIPath_Parse("lci:/foo/, &pointer); + * PARCURISegment *segment = parcURISegment_Create(3, "bar"); + * path = parcURIPath_Append(path, segment); + * + * // use the full path as necessary + * + * parcURIPath_Release(&path); + * parcURISegment_Destroy(&segment); + * } + * @endcode + */ +PARCURIPath *parcURIPath_Append(PARCURIPath *path, const PARCURISegment *segment); + +/** + * Retrieve the URI path segment at the specified index. + * + * The index must be within the range [0, number of segments] + * prior to invocation. Otherwise, the program is terminated with LongBow trapOutOfBounds. + * + * @param [in] path A `PARCURIPath` instance to be examined. + * @param [in] index The index of the URI segment to retrieve. + * + * @return The {@link PARCURISegment} instance at the specified index. + * + * Example: + * @code + * { + * char *pointer; + * PARCURIPath *path = parcURIPath_Parse("lci:/foo/, &pointer); + * PARCURISegment *segment = parcURIPath_Get(path, 0); + * // segment will be equal to "lci:" + * + * ... + * + * parcURIPath_Release(&path); + * parcURISegment_Destroy(&segment); + * } + * @endcode + */ +PARCURISegment *parcURIPath_Get(const PARCURIPath *path, size_t index); + +/** + * Return the number of segments in the given Path. + * + * @param [in] path The `PARCURIPath` instance to be examined. + * + * @return The integer length of the path, in segments. + * + * Example: + * @code + * { + * char *pointer; + * PARCURIPath *path = parcURIPath_Parse("lci:/foo/, &pointer); + * size_t numSegments = parcURIPath_Count(path); + * ... + * parcURIPath_Release(&path); + * } + * @endcode + */ +size_t parcURIPath_Count(const PARCURIPath *path); + +/** + * Produce a null-terminated C-string representation of the specified instance. + * + * The non-null result must be freed by the caller via {@link parcMemory_Deallocate}. + * + * @param [in] path A pointer to the instance. + * + * @return NULL Memory could not be allocated. + * @return non-NULL A null-terminated string that must be deallocated via {@link parcMemory_Deallocate}. + * + * Example: + * @code + * { + * char *pointer; + * PARCURIPath *path = parcURIPath_Parse("lci:/foo/, &pointer); + * + * char *stringRep = parcURIPath_ToString(path); + * printf("Path: %s\n", stringRep); + * + * parcURIPath_Release(&path); + * } + * @endcode + */ +char *parcURIPath_ToString(const PARCURIPath *path); + +/** + * Remove N trailing segments from the given Path. + * + * @param [in,out] path The `PARCURIPath` instance being modified. + * @param [in] numberToRemove The number of segments to remove from the end. + * + * @return `PARCURIPath` The given `PARCURIPath` that has been modified in place + * @return NULL If @p numberToRemove is too large. + * + * Example: + * @code + * { + * char *pointer; + * PARCURIPath *path = parcURIPath_Parse("lci:/foo/, &pointer); + * + * ... + * path = parcURIPath_Trim(path, 1); // leaves "lci:/" + * ... + * + * parcURIPath_Release(&path); + * } + * @endcode + */ +PARCURIPath *parcURIPath_Trim(PARCURIPath *path, size_t numberToRemove); + +/** + * Build a string representation of the `PARCURIPath` stored in a {@link PARCBufferComposer} instance. + * + * @param [in] path The `PARCURIPath` instance from which the string representation is made. + * @param [in,out] composer The `PARCBufferComposer` which is modified in place with the string representation. + * + * @return `PARCBufferComposer` The modified `PARCBufferComposer` that was passed in. + * + * Example: + * @code + * { + * char *pointer; + * PARCURIPath *path = parcURIPath_Parse("/foo/bar/, &pointer); + * PARCBufferComposer *composer = parcBufferComposer_Create(); + * + * parcURIPath_BuildString(path, composer); + * + * PARCBuffer *string = parcBufferComposer_ProducerBuffer(composer); + * printf("URI: %s\n", parcBuffer_ToString(string)); + * parcBuffer_Release(&string); + * } + * @endcode + */ +PARCBufferComposer *parcURIPath_BuildString(const PARCURIPath *path, PARCBufferComposer *composer); + +/** + * Determine if a `PARCURIPath` begins with the specified URI prefix. + * + * @param [in] base The `PARCURIPath` instance which is being checked. + * @param [in] prefix The `PARCURIPath` prefix used to check as the prefix. + * + * @return true If the base is prefixed with the given `PARCURIPath` + * @return false Otherwise + * + * Example: + * @code + * { + * char *pointer; + * PARCURIPath *whole = parcURIPath_Parse("lci:/foo/bar, &pointer); + * PARCURIPath *prefix = parcURIPath_Parse("lci:/foo/, &pointer); + * + * bool isPrefix(whole, prefix); // returns true + * } + * @endcode + */ +bool parcURIPath_StartsWith(const PARCURIPath *base, const PARCURIPath *prefix); + +/** + * Determine the length of the given `PARCURIPath` instance. + * + * @param [in] path The `PARCURIPath` instance which is being examined. + * + * @return The length of the `PARCURIPath`, in terms of the number of segments. + * + * Example: + * @code + * { + * char *pointer; + * PARCURIPath *path = parcURIPath_Parse("lci:/foo/, &pointer); + * + * size_t lengthOfPath = parcURIPath_Length(path); // returns 2 + * } + * @endcode + */ +size_t parcURIPath_Length(const PARCURIPath *path); +#endif // libparc_PARCURIPath_h diff --git a/libparc/parc/algol/parc_URISegment.c b/libparc/parc/algol/parc_URISegment.c new file mode 100755 index 00000000..7261c3aa --- /dev/null +++ b/libparc/parc/algol/parc_URISegment.c @@ -0,0 +1,276 @@ +/* + * 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 <LongBow/runtime.h> + +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <ctype.h> +#include <stdarg.h> + +#include <parc/algol/parc_URISegment.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> + +struct parc_uri_segment { + PARCBuffer *buffer; + bool requiresEscaping; +}; + +static char *hexDigitsUpper = "0123456789ABCDEF"; +static char *hexDigitsLower = "0123456789abcdef"; + +// Given a value, return the low nibble as a hex character. +static char +_toHexDigit(const char value) +{ + return hexDigitsUpper[value & 0xF]; +} + +/** + * @function _fromHexDigit + * @abstract Given a hex character, return the decimal value. + * @discussion + * Given a simple character containing the ASCII value for a hexadecimal digit, return the actual value. + * + * @param hexDigit A hexadecimal digit as a character from the set of characters <code>0123456789ABCDEFabcdef</code> + * @return Return the decimal value of the given hex character. If not a hex character return a value > 15. + */ +static char +_fromHexDigit(const char hexDigit) +{ + for (char i = 0; i < 16; i++) { + if (hexDigit == hexDigitsLower[(int) i] || hexDigit == hexDigitsUpper[(int) i]) { + return i; + } + } + return -1; +} + +static const char * +_parsePercentEncoded(const char *string, unsigned char *value) +{ + char c = *string++; + if (c != 0) { + unsigned char hi = _fromHexDigit(c); + if (hi > 15) { + return NULL; + } + c = *string++; + if (c != 0) { + unsigned char lo = _fromHexDigit(c); + if (lo > 15) { + return NULL; + } + *value = (unsigned char) (hi << 4 | lo); + return string; + } + } + return NULL; +} + +#define uriPlainSegmentChar(c) (c != 0 && strchr("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~", c) != NULL) + +static PARCBufferComposer * +_parcURISegment_BuildString(const PARCURISegment *segment, PARCBufferComposer *composer) +{ + assertNotNull(composer, "Parameter must be a non-null pointer to a PARCBufferComposer."); + + for (size_t i = 0; i < parcBuffer_Limit(segment->buffer) && composer != NULL; i++) { + unsigned char c = parcBuffer_GetAtIndex(segment->buffer, i); + if (uriPlainSegmentChar(c)) { + parcBufferComposer_PutChar(composer, c); + } else { + parcBufferComposer_PutChar(composer, '%'); + parcBufferComposer_PutChar(composer, _toHexDigit(c >> 4)); + parcBufferComposer_PutChar(composer, _toHexDigit(c)); + } + } + + return composer; +} + +static void +_parcURISegment_Finalize(PARCURISegment **segmentPtr) +{ + PARCURISegment *segment = *segmentPtr; + + parcBuffer_Release(&(segment->buffer)); +} + +parcObject_ExtendPARCObject(PARCURISegment, _parcURISegment_Finalize, parcURISegment_Clone, parcURISegment_ToString, + parcURISegment_Equals, parcURISegment_Compare, NULL, NULL); + +PARCURISegment * +parcURISegment_CreateFromBuffer(PARCBuffer *buffer) +{ + PARCURISegment *result = parcObject_CreateInstance(PARCURISegment); + if (result != NULL) { + result->buffer = parcBuffer_Acquire(buffer); + } + return result; +} + +PARCURISegment * +parcURISegment_Create(size_t length, const unsigned char segment[length]) +{ + PARCURISegment *result = NULL; + + PARCBuffer *buffer = parcBuffer_Allocate(length); + if (buffer != NULL) { + parcBuffer_PutArray(buffer, length, segment); + parcBuffer_Flip(buffer); + result = parcURISegment_CreateFromBuffer(buffer); + parcBuffer_Release(&buffer); + } + return result; +} + +PARCURISegment * +parcURISegment_Parse(const char *string, const char **pointer) +{ + assertFalse(*string == '/', "Input parameter '%s' must NOT point to an initial '/' character.", string); + + unsigned char *segment = parcMemory_AllocateAndClear((strlen(string) + 1) * sizeof(unsigned char)); + assertNotNull(segment, "parcMemory_AllocateAndClear(%zu) returned NULL", (strlen(string) + 1) * sizeof(unsigned char)); + size_t length = 0; + + unsigned char *r = segment; + + const char *p = string; + while (*p && *p != '/' && *p != '?' && *p != '#') { + if (*p == '%') { + unsigned char value; + if ((p = _parsePercentEncoded(p + 1, &value)) == NULL) { + parcMemory_Deallocate((void **) &segment); + return NULL; + } + *r = value; + } else { + *r = *p++; + } + length++; + r++; + } + if (*p != 0) { + // absorb any extra slash characters. + while (p[1] == '/') { + p++; + } + } + + PARCURISegment *result = parcURISegment_Create(length, segment); + parcMemory_Deallocate((void **) &segment); + if (pointer != NULL) { + *pointer = p; + } + return result; +} + +parcObject_ImplementAcquire(parcURISegment, PARCURISegment); + +parcObject_ImplementRelease(parcURISegment, PARCURISegment); + +PARCBuffer * +parcURISegment_GetBuffer(const PARCURISegment *segment) +{ + parcBuffer_Rewind(segment->buffer); + return segment->buffer; +} + +size_t +parcURISegment_Length(const PARCURISegment *segment) +{ + parcBuffer_Rewind(segment->buffer); + return parcBuffer_Remaining(segment->buffer); +} + +bool +parcURISegment_Equals(const PARCURISegment *segmentA, const PARCURISegment *segmentB) +{ + if (segmentA == segmentB) { + return true; + } + if (segmentA == NULL || segmentB == NULL) { + return false; + } + return parcBuffer_Equals(segmentA->buffer, segmentB->buffer); +} + +PARCURISegment * +parcURISegment_Clone(const PARCURISegment *segment) +{ + assertNotNull(segment, "Parameter must be a non-null PARC_URISegment pointer."); + + PARCBuffer *copy = parcBuffer_Copy(segment->buffer); + PARCURISegment *result = parcURISegment_CreateFromBuffer(copy); + parcBuffer_Release(©); + return result; +} + +int +parcURISegment_Compare(const PARCURISegment *a, const PARCURISegment *b) +{ + if (a == NULL) { + if (b == NULL) { + return 0; + } + return -1; + } else { + if (b == NULL) { + return +1; + } + } + + if (parcURISegment_Length(a) < parcURISegment_Length(b)) { + return -1; + } + if (parcURISegment_Length(a) > parcURISegment_Length(b)) { + return +1; + } + return parcBuffer_Compare(a->buffer, b->buffer); +} + +PARCBufferComposer * +parcURISegment_BuildString(const PARCURISegment *segment, PARCBufferComposer *composer) +{ + composer = _parcURISegment_BuildString(segment, composer); + + return composer; +} + +char * +parcURISegment_ToString(const PARCURISegment *segment) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + + char *result = NULL; + + if (parcURISegment_BuildString(segment, composer)) { + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + } + parcBufferComposer_Release(&composer); + + return result; +} diff --git a/libparc/parc/algol/parc_URISegment.h b/libparc/parc/algol/parc_URISegment.h new file mode 100644 index 00000000..e7234d3f --- /dev/null +++ b/libparc/parc/algol/parc_URISegment.h @@ -0,0 +1,355 @@ +/* + * 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. + */ + +/** + * @file parc_URISegment.h + * @ingroup networking + * @brief A Universal Resource Identifier (URI) Segment + * + */ +#ifndef libparc_parc_URISegment_h +#define libparc_parc_URISegment_h + +#include <parc/algol/parc_BufferComposer.h> + +struct parc_uri_segment; +typedef struct parc_uri_segment PARCURISegment; + +/** + * Create a `PARCURISegment` instance copying data from the given pointer and length. + * + * A `PARCURISegment` is a tuple consisting of a pointer to arbitrary memory containing the first byte + * of `length` bytes as the value of the segment. + * + * Since the input parameter `pointer` points to arbitrary memory, + * this function makes a private copy of the data. + * + * @param [in] length The length of the given array pointed to by @p segment. + * @param [in] segment A pointer to an array consisting of at least @p length bytes. + * + * @return A `PARCURISegment` instance referring to the given pointer and length. + * + * Example: + * @code + * { + * PARCURISegment *segment = parcURISegment_Create(5, "Hello"); + * ... + * parcURISegment_Release(&segment); + * } + * @endcode + */ +PARCURISegment *parcURISegment_Create(size_t length, const unsigned char *segment); + +/** + * Create a `PARCURISegment` instance referencing the given {@link PARCBuffer}. + * + * A new reference to the given `PARCBuffer` is acquired. + * + * @param [in] buffer A pointer to a `PARCBuffer` instance. + * + * @return A `PARCURISegment` instance referring to the given `PARCBuffer` + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Wrap("lci:/foo/bar", 12, 0, 12); + * PARCURISegment *segment = parcURISegment_CreateFromBuffer(buffer); + * ... + * parcURISegment_Release(&segment); + * } + * @endcode + */ +PARCURISegment *parcURISegment_CreateFromBuffer(PARCBuffer *buffer); + +/** + * Increase the number of references to a `PARCURISegment`. + * + * Note that new `PARCURISegment` is not created, + * only that the given `PARCURISegment` reference count is incremented. + * Discard the reference by invoking {@link parcURISegment_Release}. + * + * @param [in] segment A pointer to the original instance. + * + * @return The value of the input parameter @p instance. + * + * Example: + * @code + * { + * PARCURISegment *x = parcURISegment_Create(5, "Hello"); + * + * PARCURISegment *x2 = parcURISegment_Acquire(x); + * + * parcURISegment_Release(&x); + * parcURISegment_Release(&x2); + * } + * @endcode + * + * @see {@link parcURISegment_Release} + */ +PARCURISegment *parcURISegment_Acquire(const PARCURISegment *segment); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] segmentPtr A pointer to a pointer to the instance to release. + * + * Example: + * @code + * { + * PARCURISegment *x = parcURISegment_Create(5, "Hello"); + * + * parcURISegment_Release(&x); + * } + * @endcode + */ +void parcURISegment_Release(PARCURISegment **segmentPtr); + +/** + * Compares two `PARCURISegment` instances for order. + * + * As strings, URI segments are compared in normal lexographical order. This + * is analogous to strcmp(...). + * + * @param [in] a A `PARCURISegment` pointer, or NULL. + * @param [in] b A `PARCURISegment` pointer, or NULL. + * + * @return A negative integer, zero, or a positive integer as a is less than, equal to, or greater than b, accordingly. + * + * Example: + * @code + * { + * PARCURISegment *segmentA = parcURISegment_Create(6, "HelloA"); + * PARCURISegment *segmentB = parcURISegment_Create(6, "HelloB"); + * int cmp = parcURISegment_Compare(segmentA, segmentB); + * // cmp will be a negative integer since "HelloA" < "HelloB" + * } + * @endcode + */ +int parcURISegment_Compare(const PARCURISegment *a, const PARCURISegment *b); + +/** + * Create an independant copy the given `PARCURISegment` + * + * A new URI segment is created as a complete copy of the original. + * + * @param [in] segment A pointer to a `PARCURISegment` instance. + * + * @return NULL Memory could not be allocated. + * @return non-NULL A pointer to a new `PARCURISegment` instance. + * + * Example: + * @code + * { + * PARCURISegment *segment = parcURISegment_Create(5, "Hello"); + * PARCURISegment *copy = parcURISegment_Clone(segment); + * + * // use either segment or copy as needed + * + * parcBuffer_Release(©); + * parcBuffer_Release(&segment); + * } + * @endcode + * + */ +PARCURISegment *parcURISegment_Clone(const PARCURISegment *segment); + +/** + * Determine if two `PARCURISegment` instances are equal. + * + * The following equivalence relations on non-null `PARCURISegment` instances are maintained + * + * * It is reflexive: for any non-null reference value x, `parcURISegment_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `parcURISegment_Equals(x, y)` must return true if and only if + * `parcURISegment_Equals(y x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcURISegment_Equals(x, y)` returns true and + * `parcURISegment_Equals(y, z)` returns true, + * then `parcURISegment_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `parcURISegment_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `parcURISegment_Equals(x, NULL)` must return false. + * + * + * @param [in] x A pointer to a `PARCURISegment` instance. + * @param [in] y A pointer to a `PARCURISegment` instance. + * + * @return true `PARCURISegment` x and y are equal. + * @return false `PARCURISegment` x and y are not equal. + * + * Example: + * @code + * { + * PARCURISegment *segmentA = parcURISegment_Create(5, "Hello"); + * PARCURISegment *segmentB = parcURISegment_Create(5, "Hello"); + * + * if (parcURISegment_Equals(segmentA, segmentB)) { + * printf("The URI segments are equal\n"); + * } else { + * printf("The URI segments are NOT equal\n"); + * } + * + * parcURISegment_Equals(&segmentA); + * parcURISegment_Equals(&segmentB); + * } + * @endcode + */ +bool parcURISegment_Equals(const PARCURISegment *x, const PARCURISegment *y); + +/** + * Parse a single URI segment. + * + * The input parameter string must point to a '/' which indicates the beginning of a segment. + * The segment is terminated by a null byte or a '?' or '#' character and may may be zero length. + * The output parameter pointer will be assigned the address of the first character that is not part of the parsed segment. + * + * The returned value must be deallocated via {@link parcMemory_Deallocate}. + * + * @param [in] string Pointer to the beginning of the segment + * @param [in,out] pointer Will be assigned the address of the first character that is not part of the parsed segment. + * + * @return An allocated `PARCURISegment` the must be deallocated via `parcMemory_Deallocate`. + * + * Example: + * @code + * { + * char *pointer; + * PARCURISegment *segment = parcURISegment_Parse("lci:/a/b/", &pointer); + * ... + * parcBuffer_Release(&segment); + * } + * @endcode + */ +PARCURISegment *parcURISegment_Parse(const char *string, const char **pointer); + +/** + * Get the {@link PARCBuffer} containing the content of the given URI segment. + * + * The PARCBuffer is always rewound (see {@link parcBuffer_Rewind}). + * + * @param [in] segment A `PARCURISegment` instance from which the buffer will be extracted. + * + * @return A `PARCBuffer` instance containing the URI segment bytes that must be freed via {@link parcBuffer_Release()} + * + * Example: + * @code + * { + * PARCURISegment *segment = parcURISegment_Create(5, "Hello"); + * PARCBuffer *segBuffer = parcURISegment_GetBuffer(segment); + * // use or display the segment buffer as needed... + * + * parcURISegment(&segment); + * } + * @endcode + */ +PARCBuffer *parcURISegment_GetBuffer(const PARCURISegment *segment); + +/** + * Append a representation of the specified instance to the given {@link PARCBufferComposer}. + * + * The URI representation is escape-encoded for all characters specified + * + * @param [in] component A pointer to the instance to be appended to @p composer. + * @param [in,out] composer A `PARCBufferComposer` to which this URI segment is appended. + * + * @return NULL Cannot allocate memory. + * @return non-NULL The given `PARCBufferComposer`. + * + * Example: + * @code + * { + * PARCBufferComposer *result = parcBufferComposer_Create(); + * + * parcURISegment_BuildString(instance, result); + * + * PARCBuffer *string = parcBufferComposer_FinalizeBuffer(result); + * printf("URI: %s\n", parcBuffer_ToString(string)); + * parcBuffer_Release(&string); + * + * parcBufferComposer_Release(&result); + * } + * @endcode + */ +PARCBufferComposer *parcURISegment_BuildString(const PARCURISegment *component, PARCBufferComposer *composer); + +/** + * Produce a null-terminated string representation of the specified `PARCURISegment` instance. + * + * The result must be freed by the caller via {@link parcMemory_Deallocate}. + * + * The result is percent-encoding if necessary. + * A `PARCURISegment` may contain characters that are not permitted in the path in their native form. + * These characters must be 'percent encoded' using the '%' followed by the hexadecimal value of the character. + * For consistency, percent-encoded octets in the ranges of + * ALPHA (%41-%5A and %61-%7A), + * DIGIT (%30-%39), + * hyphen (%2D), + * period (%2E), + * underscore (%5F), + * or tilde (%7E) + * should not be created by URI producers and, + * when found in a URI, + * should be decoded to their corresponding unreserved characters by URI normalizers. + * + * @param [in] segment A pointer to the instance. + * + * @return An allocated, null-terminated C string that must be deallocated via {@link parcMemory_Deallocate}. + * + * Example: + * @code + * { + * PARCURISegment *instance = parcURISegment_Create(5, "Hello"); + * + * char *string = parcURISegment_ToString(instance); + * + * printf("Hello: %s\n", string); + * parcMemory_Deallocate((void **)&string); + * + * parcURISegment_Release(&instance); + * } + * @endcode + * + * @see parcURISegment_Parse + */ +char *parcURISegment_ToString(const PARCURISegment *segment); + +/** + * Get the length, in bytes, of the given `PARCURISegment`. + * + * @param [in] segment A pointer to the segment to inspect. + * + * @return The length, in bytes, of the given `PARCURISegment`. + * + * Example: + * @code + * { + * PARCURISegment *instance = parcURISegment_Create(5, "Hello"); + * size_t length = parcURISegment_Length(instance); + * // length will be 5 + * } + * @endcode + */ +size_t parcURISegment_Length(const PARCURISegment *segment); +#endif // libparc_parc_URISegment_h diff --git a/libparc/parc/algol/parc_Unsigned.c b/libparc/parc/algol/parc_Unsigned.c new file mode 100644 index 00000000..839f6b2b --- /dev/null +++ b/libparc/parc/algol/parc_Unsigned.c @@ -0,0 +1,176 @@ +/* + * 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 <stdio.h> +#include <config.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_DisplayIndented.h> + +#include <parc/algol/parc_Unsigned.h> + +struct PARCUnsigned { + unsigned x; +}; + +static bool +_parcUnsigned_Destructor(PARCUnsigned **instancePtr) +{ + return true; +} + +parcObject_ImplementAcquire(parcUnsigned, PARCUnsigned); + +parcObject_ImplementRelease(parcUnsigned, PARCUnsigned); + +parcObject_Override(PARCUnsigned, PARCObject, + .destructor = (PARCObjectDestructor *) _parcUnsigned_Destructor, + .copy = (PARCObjectCopy *) parcUnsigned_Copy, + .display = (PARCObjectDisplay *) parcUnsigned_Display, + .toString = (PARCObjectToString *) parcUnsigned_ToString, + .equals = (PARCObjectEquals *) parcUnsigned_Equals, + .compare = (PARCObjectCompare *) parcUnsigned_Compare, + .hashCode = (PARCObjectHashCode *) parcUnsigned_HashCode, + .toJSON = (PARCObjectToJSON *) parcUnsigned_ToJSON, + .display = (PARCObjectDisplay *) parcUnsigned_Display); + +void +parcUnsigned_AssertValid(const PARCUnsigned *instance) +{ + assertTrue(parcUnsigned_IsValid(instance), + "PARCUnsigned is not valid."); +} + +PARCUnsigned * +parcUnsigned_Create(unsigned x) +{ + PARCUnsigned *result = parcObject_CreateInstance(PARCUnsigned); + if (result != NULL) { + result->x = x; + } + return result; +} + +int +parcUnsigned_Compare(const PARCUnsigned *val, const PARCUnsigned *other) +{ + int result = 0; + + if (val == NULL) { + if (other != NULL) { + result = -1; + } + } else if (other == NULL) { + result = 1; + } else { + parcUnsigned_OptionalAssertValid(val); + parcUnsigned_OptionalAssertValid(other); + + if (val->x < other->x) { + return -1; + } else if (val->x > other->x) { + return 1; + } + } + + return result; +} + +PARCUnsigned * +parcUnsigned_Copy(const PARCUnsigned *original) +{ + PARCUnsigned *result = parcUnsigned_Create(original->x); + + return result; +} + +void +parcUnsigned_Display(const PARCUnsigned *instance, int indentation) +{ + parcDisplayIndented_PrintLine(indentation, "PARCSUnsingned@%p {", instance); + parcDisplayIndented_PrintLine(indentation + 1, "%d", instance->x); + parcDisplayIndented_PrintLine(indentation, "}"); +} + +bool +parcUnsigned_Equals(const PARCUnsigned *x, const PARCUnsigned *y) +{ + bool result = false; + + if (x == y) { + result = true; + } else if (x == NULL || y == NULL) { + result = false; + } else { + parcUnsigned_OptionalAssertValid(x); + parcUnsigned_OptionalAssertValid(y); + + if (x->x == y->x) { + result = true; + } + } + + return result; +} + +PARCHashCode +parcUnsigned_HashCode(const PARCUnsigned *x) +{ + PARCHashCode result = 0; + + result = parcHashCode_Hash((uint8_t *) &(x->x), sizeof(x->x)); + + return result; +} + +bool +parcUnsigned_IsValid(const PARCUnsigned *x) +{ + bool result = false; + + if (x != NULL) { + result = true; + } + + return result; +} + +PARCJSON * +parcUnsigned_ToJSON(const PARCUnsigned *x) +{ + PARCJSON *result = parcJSON_Create(); + + return result; +} + +char * +parcUnsigned_ToString(const PARCUnsigned *x) +{ + int length = snprintf(NULL, 0, "%d", x->x); + char*str = malloc(length + 1); + snprintf(str, length + 1, "%d", x->x); + return str; +} + +unsigned +parcUnsigned_GetUnsigned(const PARCUnsigned *x) +{ + parcUnsigned_OptionalAssertValid(x); + + return x->x; +} diff --git a/libparc/parc/algol/parc_Unsigned.h b/libparc/parc/algol/parc_Unsigned.h new file mode 100755 index 00000000..b61195e4 --- /dev/null +++ b/libparc/parc/algol/parc_Unsigned.h @@ -0,0 +1,91 @@ +/* + * 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. + */ + +/** + * @file parc_Unsigned.h + * @ingroup types + * @brief <#Brief Description#> + * + * <#Detailed Description#> + * + */ +#ifndef PARCLibrary_parc_Unsigned +#define PARCLibrary_parc_Unsigned + +#include <parc/algol/parc_JSON.h> +#include <parc/algol/parc_HashCode.h> + +typedef struct PARCUnsigned PARCUnsigned; +extern parcObjectDescriptor_Declaration(PARCUnsigned); + +/** + */ +PARCUnsigned *parcUnsigned_Acquire(const PARCUnsigned *instance); + +#ifdef PARCLibrary_DISABLE_VALIDATION +# define parcUnsigned_OptionalAssertValid(_instance_) +#else +# define parcUnsigned_OptionalAssertValid(_instance_) parcUnsigned_AssertValid(_instance_) +#endif + +/** + */ +void parcUnsinged_AssertValid(const PARCUnsigned *instance); + +/** + */ +PARCUnsigned *parcUnsigned_Create(unsigned x); + + +/** + */ +int parcUnsigned_Compare(const PARCUnsigned *instance, const PARCUnsigned *other); + +/** + */ +PARCUnsigned *parcUnsigned_Copy(const PARCUnsigned *original); + +/** + */ +void parcUnsigned_Display(const PARCUnsigned *instance, int indentation); + +/** + */ +bool parcUnsigned_Equals(const PARCUnsigned *x, const PARCUnsigned *y); + +/** + */ +PARCHashCode parcUnsigned_HashCode(const PARCUnsigned *instance); + +/** + */ +bool parcUnsigned_IsValid(const PARCUnsigned *instance); + +/** + */ +void parcUnsigned_Release(PARCUnsigned **instancePtr); + +/** + */ +PARCJSON *parcUnsigned_ToJSON(const PARCUnsigned *instance); + +/** + */ +char *parcUnsigned_ToString(const PARCUnsigned *instance); + +/** + */ +unsigned parcUnsigned_GetUnsigned(const PARCUnsigned *istance); +#endif diff --git a/libparc/parc/algol/parc_Varint.c b/libparc/parc/algol/parc_Varint.c new file mode 100755 index 00000000..7a593f45 --- /dev/null +++ b/libparc/parc/algol/parc_Varint.c @@ -0,0 +1,543 @@ +/* + * 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 <LongBow/runtime.h> + +#include <stdio.h> +#include <stdlib.h> + +#define __STDC_FORMAT_MACROS +#include <inttypes.h> + +#include <parc/algol/parc_Varint.h> +#include <parc/algol/parc_Memory.h> + +struct parc_varint { + uint64_t value; +}; + +PARCVarint * +parcVarint_Create(void) +{ + PARCVarint *result = parcMemory_AllocateAndClear(sizeof(PARCVarint)); + if (result != NULL) { + result->value = 0; + } + return result; +} + +PARCVarint * +parcVarint_DecodeBuffer(PARCBuffer *buffer, size_t length) +{ + assertNotNull(buffer, "Parameter must be a non-null PARCBuffer pointer."); + assertTrue(length < sizeof(size_t), "Length must be less then or equal to %zd", sizeof(size_t)); + assertTrue(length <= parcBuffer_Remaining(buffer), "Buffer does not contain at least %zd bytes", length); + + PARCVarint *result = parcVarint_Create(); + assertNotNull(result, "PARCVarint out of memory."); + + for (size_t i = 0; i < length; i++) { + parcVarint_ShiftLeft(result, 8); + parcVarint_OrUint8(result, parcBuffer_GetUint8(buffer)); + } + + return result; +} + +PARCVarint * +parcVarint_DecodeElasticByteBuffer(const PARCBuffer *buffer, size_t length) +{ + assertNotNull(buffer, "Parameter must be a non-null PARCBuffer pointer."); + assertTrue(length < sizeof(size_t), "Length must be less then or equal to %zd", sizeof(size_t)); + + PARCVarint *result = parcVarint_Create(); + assertNotNull(result, "PARCVarint out of memory."); + + for (size_t i = 0; i < length; i++) { + parcVarint_ShiftLeft(result, 8); + parcVarint_OrUint8(result, parcBuffer_GetAtIndex(buffer, i)); + } + + return result; +} + +PARCVarint * +parcVarint_Set(PARCVarint *varint, uint64_t newValue) +{ + varint->value = newValue; + return varint; +} + +PARCVarint * +parcVarint_FromElasticByteBuffer(const PARCBuffer *buffer) +{ + assertNotNull(buffer, "Parameter must be a non-null PARCBuffer pointer."); + PARCVarint *result = parcVarint_Create(); + + size_t length = parcBuffer_Remaining(buffer); + + for (size_t i = 0; i < length; i++) { + parcVarint_ShiftLeft(result, 8); + parcVarint_OrUint8(result, parcBuffer_GetAtIndex(buffer, i)); + } + + return result; +} + +PARCVarint * +parcVarint_FromUTF8ByteBuffer(const PARCBuffer *buffer) +{ + assertNotNull(buffer, "Parameter must not be NULL."); + PARCVarint *result = parcVarint_Create(); + + if (result != NULL) { + size_t length = parcBuffer_Remaining(buffer); + + for (size_t i = 0; i < length; i++) { + parcVarint_Multiply(result, 10); + parcVarint_Add(result, parcBuffer_GetAtIndex(buffer, i) - '0'); + } + } + + return result; +} + +PARCVarint * +parcVarint_FromUTF8Buffer(PARCBuffer *buffer) +{ + assertNotNull(buffer, "Parameter must be a non-null PARCBuffer pointer."); + PARCVarint *result = parcVarint_Create(); + + if (result != NULL) { + size_t length = parcBuffer_Limit(buffer); + + for (size_t i = 0; i < length; i++) { + parcVarint_Multiply(result, 10); + parcVarint_Add(result, parcBuffer_GetAtIndex(buffer, i) - '0'); + } + } + + return result; +} + +/** + * + * @param uint + * @return + */ +PARCVarint * +parcVarint_FromUint8(uint8_t uint) +{ + return parcVarint_FromUint32(uint); +} + +/** + * + * @param uint + * @return + */ +PARCVarint * +parcVarint_FromUint32(uint32_t uint) +{ + return parcVarint_FromUint64(uint); +} + +/** + * + * @param uint + * @return + */ +PARCVarint * +parcVarint_FromUint64(uint64_t uint) +{ + PARCVarint *result = parcMemory_AllocateAndClear(sizeof(PARCVarint)); + if (result != NULL) { + result->value = uint; + } + return result; +} + +/** + * + * @param varintP + */ +void +parcVarint_Destroy(PARCVarint **varintP) +{ + assertNotNull(varintP, "Parameter must be a non-null pointer to a pointer to a PARCVarint"); + assertNotNull(*varintP, "Parameter must be a non-null pointer to a PARCVarint"); + + parcMemory_Deallocate((void **) varintP); + *varintP = NULL; +} + +/** + * Shift the value {@code bits} to the left. + * + * @param varint + * @param bits + * @return + */ +PARCVarint * +parcVarint_ShiftLeft(PARCVarint *varint, int bits) +{ + assertNotNull(varint, "Parameter must be a non-null pointer to a PARCVarint."); + varint->value <<= bits; + + return varint; +} + +PARCVarint * +parcVarint_Add(PARCVarint *varint, int addend) +{ + assertNotNull(varint, "Parameter must be a non-null pointer to a PARCVarint."); + varint->value += addend; + + return varint; +} + +PARCVarint * +parcVarint_Subtract(PARCVarint *varint, int subtrahend) +{ + assertNotNull(varint, "Parameter must be a non-null pointer to a PARCVarint."); + varint->value -= subtrahend; + + return varint; +} + +PARCVarint * +parcVarint_Multiply(PARCVarint *varint, int multiplicand) +{ + assertNotNull(varint, "Parameter must be a non-null pointer to a PARCVarint."); + varint->value *= multiplicand; + + return varint; +} + +PARCVarint * +parcVarint_Divide(PARCVarint *varint, int divisor) +{ + assertNotNull(varint, "Parameter must be a non-null pointer to a PARCVarint."); + varint->value /= divisor; + + return varint; +} + +/** + * Shift the value {@code bits} to the right. + * + * @param varint + * @param bits + * @return + */ +PARCVarint * +parcVarint_ShiftRight(PARCVarint *varint, int bits) +{ + assertNotNull(varint, "Parameter must be a non-null pointer to a PARCVarint."); + varint->value >>= bits; + return varint; +} + +/** + * Perform an AND operation on the given {@link PARCVarint} with the supplied {@code operand}, + * leaving the result in {@code varint}. + * + * @param varint + * @param operand + * @return + */ +PARCVarint * +parcVarint_And(PARCVarint *varint, PARCVarint *operand) +{ + assertNotNull(varint, "Parameter varint must not be NULL."); + assertNotNull(operand, "Parameter operand must not be NULL."); + varint->value &= operand->value; + return varint; +} + +/** + * + * @param varint + * @param operand + * @return + */ +PARCVarint * +parcVarint_AndUint8(PARCVarint *varint, uint8_t operand) +{ + assertNotNull(varint, "Parameter varint must not be NULL."); + varint->value &= operand; + return varint; +} + +/** + * + * @param varint + * @param operand + * @return + */ +PARCVarint * +parcVarint_AndUint16(PARCVarint *varint, uint16_t operand) +{ + assertNotNull(varint, "Parameter varint must not be NULL."); + varint->value &= operand; + return varint; +} + +/** + * + * @param varint + * @param operand + * @return + */ +PARCVarint * +parcVarint_AndUint32(PARCVarint *varint, uint32_t operand) +{ + assertNotNull(varint, "Parameter varint must not be NULL."); + varint->value &= operand; + return varint; +} + +/** + * + * @param varint + * @param operand + * @return + */ +PARCVarint * +parcVarint_AndUint64(PARCVarint *varint, uint64_t operand) +{ + assertNotNull(varint, "Parameter must be a non-null PARCVarint pointer."); + varint->value &= operand; + return varint; +} + +/** + * + * @param varint + * @param operand + * @return + */ +PARCVarint * +parcVarint_Or(PARCVarint *varint, PARCVarint *operand) +{ + assertNotNull(varint, "Parameter must be a non-null PARCVarint pointer."); + varint->value |= operand->value; + return varint; +} + +/** + * Perform an OR operation on the low 8-bits in the given {@link PARCVarint}. + * + * @param varint + * @param operand + * @return + */ +PARCVarint * +parcVarint_OrUint8(PARCVarint *varint, uint8_t operand) +{ + assertNotNull(varint, "Parameter varint must not be NULL."); + varint->value |= operand; + return varint; +} + +/** + * Perform an OR operation on the low 16-bits in the given {@link PARCVarint}. + * + * @param varint + * @param operand + * @return + */ +PARCVarint * +parcVarint_OrUint16(PARCVarint *varint, uint16_t operand) +{ + assertNotNull(varint, "Parameter varint must not be NULL."); + varint->value |= operand; + return varint; +} + +/** + * Perform an OR operation on the low 32-bits in the given {@link PARCVarint}. + * + * @param varint + * @param operand + * @return + */ +PARCVarint * +parcVarint_OrUint32(PARCVarint *varint, uint32_t operand) +{ + assertNotNull(varint, "Parameter varint must not be NULL."); + varint->value |= operand; + return varint; +} + +/** + * Perform an OR operation on the low 64-bits in the given {@link PARCVarint}. + * + * @param varint + * @param operand + * @return + */ +PARCVarint * +parcVarint_OrUint64(PARCVarint *varint, uint64_t operand) +{ + assertNotNull(varint, "Parameter varint must not be NULL."); + varint->value |= operand; + return varint; +} + +/** + * Return {@code true} (non-zero) if the two {@link PARCVarint} structures contain equal data. + * + * @param varint + * @param operand + * @return + */ +int +parcVarint_Equals(PARCVarint *varint, PARCVarint *operand) +{ + assertNotNull(varint, "Parameter varint must not be NULL."); + return varint->value == operand->value; +} + +/** + * + * @param varint + * @param value + * @return + */ +int +parcVarint_EqualsUint64(PARCVarint *varint, uint64_t value) +{ + assertNotNull(varint, "Parameter varint must not be NULL."); + return varint->value == value; +} + +/** + * + * @param varint + * @param value + * @return + */ +int +parcVarint_EqualsUint32(PARCVarint *varint, uint32_t value) +{ + assertNotNull(varint, "Parameter varint must not be NULL."); + return parcVarint_EqualsUint64(varint, (uint64_t) value); +} + +/** + * + * @param varint + * @param value + * @return + */ +int +parcVarint_EqualsUint16(PARCVarint *varint, uint16_t value) +{ + assertNotNull(varint, "Parameter varint must not be NULL."); + return parcVarint_EqualsUint64(varint, (uint64_t) value); +} + +/** + * + * @param varint + * @param value + * @return + */ +int +parcVarint_EqualsUint8(PARCVarint *varint, uint8_t value) +{ + assertNotNull(varint, "Parameter varint must not be NULL."); + return parcVarint_EqualsUint64(varint, (uint64_t) value); +} + +/** + * Produce the 8 low-order bits of this {@code PARCVarint}. + * + * @param varint + * @return + */ +uint8_t +parcVarint_AsUint8(const PARCVarint *varint) +{ + assertNotNull(varint, "Parameter varint must not be NULL."); + return (uint8_t) varint->value; +} + +/** + * Produce the 16 low-order bits of this {@code PARCVarint}. + * + * @param varint + * @return + */ +uint16_t +parcVarint_AsUint16(const PARCVarint *varint) +{ + assertNotNull(varint, "Parameter varint must not be NULL."); + return (uint16_t) varint->value; +} + +/** + * Produce the 32 low-order bits of this {@code PARCVarint}. + * + * @param varint + * @return + */ +uint32_t +parcVarint_AsUint32(const PARCVarint *varint) +{ + assertNotNull(varint, "Parameter varint must not be NULL."); + return (uint32_t) varint->value; +} + +uint64_t +parcVarint_AsUint64(const PARCVarint *varint) +{ + assertNotNull(varint, "Parameter varint must not be NULL."); + return varint->value; +} + +/** + * Produce the value of this {@code PARCVarint} as a {@code size_t} value. + * + * @param varint + * @return + */ +size_t +parcVarint_AsSize(const PARCVarint *varint) +{ + assertNotNull(varint, "Parameter varint must not be NULL."); + return (size_t) varint->value; +} + +/** + * Produce a string representation of this {@link PARCVarint}. + * The returned value must be freed by the called using the {@code stdlib.h} {@code free}. + * @param string + * @param varint + * @return + */ +char * +parcVarint_ToString(char **string, PARCVarint *varint) +{ + assertNotNull(varint, "Parameter varint must not be NULL."); + int nwritten = asprintf(string, "%" PRIu64, varint->value); + assertTrue(nwritten >= 0, "Error calling asprintf"); + return *string; +} diff --git a/libparc/parc/algol/parc_Varint.h b/libparc/parc/algol/parc_Varint.h new file mode 100755 index 00000000..6d7e60b8 --- /dev/null +++ b/libparc/parc/algol/parc_Varint.h @@ -0,0 +1,478 @@ +/* + * 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. + */ + +/** + * @file parc_Varint.h + * @ingroup datastructures + * @brief A Variable Length Integer. + * + * A variable length integer. + * + * + * Example: + * @code + * <#example#> + * @endcode + */ +#ifndef libparc_parc_VarInt_h +#define libparc_parc_VarInt_h + +#include <stdint.h> + +#include <parc/algol/parc_Buffer.h> + +/** + * A variable length integer value. + * <em> + * This particular implementation is limited to a 64 bit value. + * </em> + */ +struct parc_varint; +typedef struct parc_varint PARCVarint; + + +#define PARC_VARINT_INIT { 0 } + +/** + * Create a new instance of `PARCVarint` + * + * @return A `PARCVarint` pointer, which must be deallocated via {@link parcVarint_Destroy()}. + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCVarint *parcVarint_Create(void); + +/** + * Decode an instance of {@link PARCBuffer} of length @p length into a new instance of `PARCVarInt` + * + * @param [in] buffer A pointer to a `PARCBuffer`. + * @param [in] length The number of bytes to decode. + * @return A `PARCVarint` pointer, which must be deallocated via {@link parcVarint_Destroy()}. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode */ +PARCVarint *parcVarint_DecodeBuffer(PARCBuffer *buffer, size_t length); + +/** + * Decode an instance of {@link PARCBuffer} of length @p length into a new instance of `PARCVarInt` + * + * @param [in] buffer A pointer to a `PARCBuffer`. + * @param [in] length The number of bytes to decode. + * @return A `PARCVarint` pointer, which must be deallocated via {@link parcVarint_Destroy()}. + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCVarint *parcVarint_DecodeElasticByteBuffer(const PARCBuffer *buffer, size_t length); + +/** + * Create a new `PARCVarint` from bytes in a {@link PARCBuffer}. + * + * @param [in] buffer A pointer to a `PARCBuffer`. + * @return A `PARCVarint` pointer, which must be deallocated via {@link parcVarint_Destroy()}. + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCVarint *parcVarint_FromElasticByteBuffer(const PARCBuffer *buffer); + +/** + * Create a `PARCVarint` with the value decoded from the given {@link PARCBuffer} + * containing a UTF-8 string encoded number. + * + * @param [in] buffer A pointer to a `PARCBuffer` containing a UTF-8 string encoded number. + * @return A `PARCVarint` pointer, which must be deallocated via {@link parcVarint_Destroy()}. + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCVarint *parcVarint_FromUTF8Buffer(PARCBuffer *buffer); + +/** + * Create a `PARCVarint` with the value decoded from the given {@link PARCBuffer} + * containing a UTF-8 string encoded number. + * + * @param [in] buffer A pointer to a `PARCBuffer` containing a UTF-8 string encoded number. + * @return A `PARCVarint` pointer, which must be deallocated via {@link parcVarint_Destroy()}. + * + */ +PARCVarint *parcVarint_FromUTF8ByteBuffer(const PARCBuffer *buffer); + + +extern PARCVarint *parcVarint_FromUint8(uint8_t uint); + +extern PARCVarint *parcVarint_FromUint32(uint32_t uint); + +extern PARCVarint *parcVarint_FromUint64(uint64_t uint); + +extern PARCVarint *parcVarint_ShiftLeft(PARCVarint *varint, int bits); + +extern PARCVarint *parcVarint_ShiftRight(PARCVarint *varint, int bits); + +extern PARCVarint *parcVarint_And(PARCVarint *varint, PARCVarint *operand); + +extern PARCVarint *parcVarint_AndUint8(PARCVarint *varint, uint8_t operand); + +extern PARCVarint *parcVarint_AndUint16(PARCVarint *varint, uint16_t operand); + +extern PARCVarint *parcVarint_AndUint32(PARCVarint *varint, uint32_t operand); + +extern PARCVarint *parcVarint_AndUint64(PARCVarint *varint, uint64_t operand); + +extern PARCVarint *parcVarint_Or(PARCVarint *varint, PARCVarint *operand); + +extern PARCVarint *parcVarint_OrUint8(PARCVarint *varint, uint8_t operand); + +extern PARCVarint *parcVarint_OrUint16(PARCVarint *varint, uint16_t operand); + +extern PARCVarint *parcVarint_OrUint32(PARCVarint *varint, uint32_t operand); + +extern PARCVarint *parcVarint_OrUint64(PARCVarint *varint, uint64_t operand); + +/** + * Determine if two `PARCVarint` instances are equal. + * + * The following equivalence relations on non-null `PARCVarint` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, `PARCVarint_Equals(x, x)` + * must return true. + * + * * It is symmetric: for any non-null reference values x and y, + * `parcVarint_Equals(x, y)` must return true if and only if + * `parcVarint_Equals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcVarint_Equals(x, y)` returns true and + * `parcVarint_Equals(y, z)` returns true, + * then `parcVarint_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple + * invocations of `parcVarint_Equals(x, y)` consistently return true or + * consistently return false. + * + * * For any non-null reference value x, `parcVarint_Equals(x, NULL)` must + * return false. + * + * @param varint A pointer to a `PARCVarint` instance. + * @param operand A pointer to a `PARCVarint` instance. + * @return true if the two `PARCVarint` instances are equal. + * + * Example: + * @code + * { + * PARCVarint *a = parcVarint_Create(); + * PARCVarint *b = parcVarint_Create(); + * + * if (parcVarint_Equals(a, b)) { + * // true + * } else { + * // false + * } + * } + * @endcode + */ + +extern int parcVarint_Equals(PARCVarint *varint, PARCVarint *operand); + +/** + * Determine if a `PARCVarint` instance is equal to a 64bit integer. + * + * The following equivalence relations on non-null `PARCVarint` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, `PARCVarint_Uint64Equals(x, x)` + * must return true. + * + * * It is NOT symmetric: for any non-null reference values x and y, + * `parcVarint_Uint64Equals(x, y)` may not return true even if + * `parcVarint_Uint64Equals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcVarint_Uint64Equals(x, y)` returns true and + * `parcVarint_Uint64Equals(y, z)` returns true, + * then `parcVarint_Uint64Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple + * invocations of `parcVarint_Uint64Equals(x, y)` consistently return true or + * consistently return false. + * + * * For any non-null reference value x, `parcVarint_Uint64Equals(x, NULL)` must + * return false. + * + * @param [in] varint A pointer to a `PARCVarint` instance. + * @param [in] value A pointer to a `uint64_t` instance. + * @return true if the two instances are equal. + * + * Example: + * @code + * { + * PARCVarint *a = parcVarint_Create(); + * uint64_t *b = 10; // some integer value + * + * if (parcVarint_Uint64Equals(a, b)) { + * // true + * } else { + * // false + * } + * } + * @endcode + */ +extern int parcVarint_EqualsUint64(PARCVarint *varint, uint64_t value); + +/** + * Determine if a `PARCVarint` instance is equal to a 32 bit integer. + * + * The following equivalence relations on non-null `PARCVarint` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, `PARCVarint_Uint32Equals(x, x)` + * must return true. + * + * * It is NOT symmetric: for any non-null reference values x and y, + * `parcVarint_Uint32Equals(x, y)` may not return true even if + * `parcVarint_Uint32Equals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcVarint_Uint32Equals(x, y)` returns true and + * `parcVarint_Uint32Equals(y, z)` returns true, + * then `parcVarint_Uint32Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple + * invocations of `parcVarint_Uint32Equals(x, y)` consistently return true or + * consistently return false. + * + * * For any non-null reference value x, `parcVarint_Uint32Equals(x, NULL)` must + * return false. + * + * @param [in] varint A pointer to a `PARCVarint` instance. + * @param [in] value A pointer to a `uint32_t` instance. + * @return true if the two instances are equal. + * + * Example: + * @code + * { + * PARCVarint *a = parcVarint_Create(); + * uint32_t *b = 10; // some integer value + * + * if (parcVarint_Uint32Equals(a, b)) { + * // true + * } else { + * // false + * } + * } + * @endcode + */ +extern int parcVarint_EqualsUint32(PARCVarint *varint, uint32_t value); + +/** + * Determine if a `PARCVarint` instance is equal to a 16 bit integer. + * + * The following equivalence relations on non-null `PARCVarint` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, `PARCVarint_Uint16Equals(x, x)` + * must return true. + * + * * It is NOT symmetric: for any non-null reference values x and y, + * `parcVarint_Uint16Equals(x, y)` may not return true even if + * `parcVarint_Uint16Equals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcVarint_Uint16Equals(x, y)` returns true and + * `parcVarint_Uint16Equals(y, z)` returns true, + * then `parcVarint_Uint16Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple + * invocations of `parcVarint_Uint16Equals(x, y)` consistently return true or + * consistently return false. + * + * * For any non-null reference value x, `parcVarint_Uint16Equals(x, NULL)` must + * return false. + * + * @param [in] varint A pointer to a `PARCVarint` instance. + * @param [in] value A pointer to a `uint16_t` instance. + * @return true if the two instances are equal. + * + * Example: + * @code + * { + * PARCVarint *a = parcVarint_Create(); + * uint16_t *b = 10; // some integer value + * + * if (parcVarint_Uint16Equals(a, b)) { + * // true + * } else { + * // false + * } + * } + * @endcode + */ +extern int parcVarint_EqualsUint16(PARCVarint *varint, uint16_t value); + +/** + * Determine if a `PARCVarint` instance is equal to an 8 bit integer. + * + * The following equivalence relations on non-null `PARCVarint` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, `PARCVarint_Uint8Equals(x, x)` + * must return true. + * + * * It is NOT symmetric: for any non-null reference values x and y, + * `parcVarint_Uint8Equals(x, y)` may not return true even if + * `parcVarint_Uint8Equals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcVarint_Uint8Equals(x, y)` returns true and + * `parcVarint_Uint8Equals(y, z)` returns true, + * then `parcVarint_Uint8Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple + * invocations of `parcVarint_Uint8Equals(x, y)` consistently return true or + * consistently return false. + * + * * For any non-null reference value x, `parcVarint_Uint8Equals(x, NULL)` must + * return false. + * + * @param [in] varint A pointer to a `PARCVarint` instance. + * @param [in] value A pointer to a `uint8_t` instance. + * @return true if the two instances are equal. + * + * Example: + * @code + * { + * PARCVarint *a = parcVarint_Create(); + * uint8_t *b = 10; // some integer value + * + * if (parcVarint_Uint8Equals(a, b)) { + * // true + * } else { + * // false + * } + * } + * @endcode + */ +extern int parcVarint_EqualsUint8(PARCVarint *varint, uint8_t value); + +extern void parcVarint_Destroy(PARCVarint **varintP); + +extern char *parcVarint_ToString(char **string, PARCVarint *varint); + +extern uint8_t parcVarint_AsUint8(const PARCVarint *varint); + +extern uint16_t parcVarint_AsUint16(const PARCVarint *varint); + +extern uint32_t parcVarint_AsUint32(const PARCVarint *varint); + +/** + * Produce the 16 low-order bits of this `PARCVarint` as a `uint8_t`. + * + * @param [in] varint The inpu instance of `PARCVarint` + * @return The 16 low-order bits as a `uint8_t` + * + * Example: + * @code + * <#example#> + * @endcode + */ +uint64_t parcVarint_AsUint64(const PARCVarint *varint); + +/** + * Return the value of the given `PARCVarint` cast as a `size_t` + * + * @param [in] varint The `PARCVarint` to cast as a `size_t` + * + * @return The given `PARCVarint` cast as a `size_t` + * + * Example: + * @code + * <#example#> + * @endcode + */ +size_t parcVarint_AsSize(const PARCVarint *varint); + +/** + * Set the value of the given `PARCVarint` to the given value. + * + * @param [in,out] varint The `PARCVarint` to be modified + * @param [in] newValue The new value for the `PARCVarint` + * @return The modified `PARCVarint`. + */ +PARCVarint *parcVarint_Set(PARCVarint *varint, uint64_t newValue); + +/** + * Multiply the `PARCVarint` + * + * @param [in,out] varint The `PARCVarint` to be multiplied by the @p multiplicand + * @param [in] multiplicand The multiplicand to multiply by + * @return The `PARCVarint` used as input. + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCVarint *parcVarint_Multiply(PARCVarint *varint, int multiplicand); + +/** + * Divide the `PARCVarint` by a divisor + * + * @param [in,out] varint The `PARCVarint` to be divided by the @p divisor + * @param [in] divisor The divisor to use + * @return The `PARCVarint` used as input. + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCVarint *parcVarint_Divide(PARCVarint *varint, int divisor); +/** + * Multiply the `PARCVarint` + * + * @param [in,out] varint The `PARCVarint` to which the @p addend should be added + * @param [in] addend The number to add to the `PARCVarint` + * @return The `PARCVarint` used as input. + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCVarint *parcVarint_Add(PARCVarint *varint, int addend); + +/** + * Subtract the @p subtrahend from the `PARCVarint`. + * + * @param [in,out] varint The `PARCVarint` from which to subtract the @p subtrahend + * @param [in] subtrahend The number to subtract from the `PARCVarint` + * @return The `PARCVarint` used as input. + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCVarint *parcVarint_Subtract(PARCVarint *varint, int subtrahend); +#endif // libparc_parc_VarInt_h diff --git a/libparc/parc/algol/parc_Vector.c b/libparc/parc/algol/parc_Vector.c new file mode 100755 index 00000000..f5380eb7 --- /dev/null +++ b/libparc/parc/algol/parc_Vector.c @@ -0,0 +1,81 @@ +/* + * 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 <LongBow/runtime.h> + +#include <stdio.h> +#include <stdbool.h> +#include <string.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_SafeMemory.h> +#include <parc/algol/parc_Vector.h> + +struct parc_vector { + const void *pointer; + size_t length; +}; + +PARCVector * +parcVector_Create(const void *pointer, const size_t length) +{ + PARCVector *result = parcMemory_AllocateAndClear(sizeof(PARCVector)); + if (result != NULL) { + parcVector_Init(result, pointer, length); + } + + return result; +} + +PARCVector * +parcVector_Init(PARCVector *vector, const void *pointer, const size_t length) +{ + assertNotNull(vector, "Parameter must be a non-null PARCVector pointer"); + + vector->pointer = pointer; + vector->length = length; + return vector; +} + +void +parcVector_Destroy(PARCVector **vectorPtr) +{ + assertNotNull(vectorPtr, "Parameter must be a non-null PARCVector pointer"); + PARCVector *vector = *vectorPtr; + assertNotNull(vector, "Vector is already free or was not set.\n"); + + parcMemory_Deallocate((void **) &vector); + *vectorPtr = NULL; +} + +const void * +parcVector_GetPointer(const PARCVector *vector) +{ + assertNotNull(vector, "Parameter must be a non-null PARCVector pointer."); + return vector->pointer; +} + +size_t +parcVector_GetLength(const PARCVector *vector) +{ + assertNotNull(vector, "Parameter must be a non-null PARCVector pointer."); + + return vector->length; +} + diff --git a/libparc/parc/algol/parc_Vector.h b/libparc/parc/algol/parc_Vector.h new file mode 100755 index 00000000..825fc635 --- /dev/null +++ b/libparc/parc/algol/parc_Vector.h @@ -0,0 +1,109 @@ +/* + * 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. + */ + +/** + * @file parc_Vector.h + * @ingroup datastructures + * @brief PARC Vector + * A Vector consists of a pointer and associated length in bytes of data in memory. + * + * + */ +#ifndef libparc_parc_Vector_h +#define libparc_parc_Vector_h + +#include <stdint.h> +#include <stdbool.h> +#include <stdlib.h> + +struct parc_vector; + +/** + * A `PARCVector` is a tuple consisting of an address and a length. + * + * Example: + * @code + * <#example#> + * @endcode + */ +typedef struct parc_vector PARCVector; + +/** + * Create a new `PARCVector` consisting of a pointer and a length. + * + * @param [in] pointer A pointer to memory. + * @param [in] length The length, in bytes. + * @return An allocated `PARCVector` structure. + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCVector *parcVector_Create(const void *pointer, const size_t length); + +/** + * Initialise the given `PARCVector` to the given values. + * + * @param [in,out] vector A pointer to the instance of `PARCVector` to initialize + * @param [in] pointer A pointer to the memory with which to initialize @p vector. + * @param [in] length The length of the @p pointer contents. + * @return The pointer to the input @p vector. + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCVector *parcVector_Init(PARCVector *vector, const void *pointer, const size_t length); + +/** + * Get the memory pointer for this `PARCVector`. + * + * @param [in] vector A pointer to the instance of `PARCVector` from which to get the memory pointer. + * @return The memory pointer of @p vector + * + * Example: + * @code + * <#example#> + * @endcode + */ +const void *parcVector_GetPointer(const PARCVector *vector); + +/** + * Get the length of of the `PARCVector` + * + * @param [in] vector The `PARCVector` instance of interest. + * @return The length of @p vector. + * + * Example: + * @code + * <#example#> + * @endcode + */ +size_t parcVector_GetLength(const PARCVector *vector); + +/** + * Destroy the instance of `PARCVector` + * + * @param [in,out] vector The pointer to the pointer to the `PARCVector` instance to destroy. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void parcVector_Destroy(PARCVector **vector); +#endif // libparc_parc_Vector_h diff --git a/libparc/parc/algol/test/.gitignore b/libparc/parc/algol/test/.gitignore new file mode 100644 index 00000000..b520ab17 --- /dev/null +++ b/libparc/parc/algol/test/.gitignore @@ -0,0 +1,73 @@ +test_parc_ArrayList +test_parc_AtomicInteger +test_parc_Base64 +test_parc_BitVector +test_parc_Buffer +test_parc_BufferComposer +test_parc_BufferDictionary +test_parc_ByteArray +test_parc_ByteBuffer +test_parc_ByteBufferCodec +test_parc_ByteBuffer_Codec +test_parc_Clock +test_parc_Deque +test_parc_Dictionary +test_parc_Digester +test_parc_Digests +test_parc_Digests +test_parc_Display +test_parc_ElasticBuffer +test_parc_ElasticString +test_parc_Environment +test_parc_Event +test_parc_EventBuffer +test_parc_EventQueue +test_parc_EventScheduler +test_parc_EventSignal +test_parc_EventSocket +test_parc_EventTimer +test_parc_Extent +test_parc_ExtentArray +test_parc_FileOutputStream +test_parc_Hash +test_parc_HashCode +test_parc_HashCodeTable +test_parc_HashMap +test_parc_HashMap +test_parc_HashTable +test_parc_InputStream +test_parc_Iterator +test_parc_JSON +test_parc_JSONArray +test_parc_JSONPair +test_parc_JSONParser +test_parc_JSONValue +test_parc_KeyValue +test_parc_KeyedElement +test_parc_LinkedList +test_parc_List +test_parc_Memory +test_parc_Network +test_parc_NewByteBuffer +test_parc_Object +test_parc_ObjectImpl +test_parc_PathName +test_parc_PointerArray +test_parc_PriorityQueue +test_parc_Properties +test_parc_ReadOnlyBuffer +test_parc_SafeMemory +test_parc_SortedList +test_parc_Stack +test_parc_StdlibMemory +test_parc_String +test_parc_StructArray +test_parc_Time +test_parc_Tlv +test_parc_TreeRedBlack +test_parc_TreeMap +test_parc_URI +test_parc_VarInt +test_parc_Varint +test_parc_Vector +test_parc_VectorArray diff --git a/libparc/parc/algol/test/CMakeLists.txt b/libparc/parc/algol/test/CMakeLists.txt new file mode 100644 index 00000000..4199f3de --- /dev/null +++ b/libparc/parc/algol/test/CMakeLists.txt @@ -0,0 +1,76 @@ +# Enable gcov output for the tests +add_definitions(--coverage) +set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} " --coverage") + +configure_file(data.json data.json COPYONLY) + +set(TestsExpectedToPass + test_parc_ArrayList + test_parc_AtomicInteger + test_parc_Base64 + test_parc_BitVector + test_parc_Buffer + test_parc_BufferChunker + test_parc_BufferComposer + test_parc_ByteArray + test_parc_Clock + test_parc_Chunker + test_parc_Deque + test_parc_Dictionary + test_parc_Display + test_parc_Environment + test_parc_Event + test_parc_EventBuffer + test_parc_EventQueue + test_parc_EventScheduler + test_parc_EventSignal + test_parc_EventSocket + test_parc_EventTimer + test_parc_File + test_parc_FileChunker + test_parc_FileInputStream + test_parc_FileOutputStream + test_parc_Hash + test_parc_HashCode + test_parc_HashCodeTable + test_parc_HashMap + test_parc_InputStream + test_parc_Iterator + test_parc_JSON + test_parc_JSONArray + test_parc_JSONPair + test_parc_JSONParser + test_parc_JSONValue + test_parc_KeyValue + test_parc_KeyedElement + test_parc_LinkedList + test_parc_List + test_parc_Memory + test_parc_Network + test_parc_Object + test_parc_PathName + test_parc_PriorityQueue + test_parc_Properties + test_parc_RandomAccessFile + test_parc_ReadOnlyBuffer + test_parc_SafeMemory + test_parc_SortedList + test_parc_Stack + test_parc_StdlibMemory + test_parc_String + test_parc_Time + test_parc_TreeMap + test_parc_TreeRedBlack + test_parc_URI + test_parc_URIAuthority + test_parc_URIPath + test_parc_URISegment + test_parc_Varint + test_parc_Vector + ) + +foreach(test ${TestsExpectedToPass}) + AddTest(${test}) +endforeach() + + diff --git a/libparc/parc/algol/test/_test_parc_URI.h b/libparc/parc/algol/test/_test_parc_URI.h new file mode 100644 index 00000000..01d3afae --- /dev/null +++ b/libparc/parc/algol/test/_test_parc_URI.h @@ -0,0 +1,57 @@ +/* + * 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. + */ + +/* + * _test_parc_URI.h + * PARC Algol + */ + +#ifndef PARC_Algol__test_parc_URI_h +#define PARC_Algol__test_parc_URI_h + +#define URI_SCHEME "lci" + +#define URI_AUTHORITY_USERINFO "user:pass" +#define URI_AUTHORITY_USERINFO_2 "user2:pass2" +#define URI_AUTHORITY_HOSTNAME "parc.com" +#define URI_AUTHORITY_HOSTNAME_2 "xerox.com" +#define URI_AUTHORITY_LITERAL_HOSTNAME "127.0.0.1" +#define URI_AUTHORITY_LITERAL_HOSTNAME6 "[::0]" +#define URI_AUTHORITY_LITERAL_HOSTNAME6_2 "[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]" +#define URI_AUTHORITY_PORT_1 "1234" +#define URI_AUTHORITY_PORT_2 "5678" + +#define URI_AUTHORITY URI_AUTHORITY_USERINFO "@" URI_AUTHORITY_HOSTNAME ":" URI_AUTHORITY_PORT_1 +#define URI_AUTHORITY_DIFFERENT_PORT URI_AUTHORITY_USERINFO "@" URI_AUTHORITY_HOSTNAME ":" URI_AUTHORITY_PORT_2 +#define URI_AUTHORITY_DIFFERENT_HOST URI_AUTHORITY_USERINFO "@" URI_AUTHORITY_HOSTNAME_2 ":" URI_AUTHORITY_PORT_1 +#define URI_AUTHORITY_LITERAL_HOST URI_AUTHORITY_USERINFO "@" URI_AUTHORITY_LITERAL_HOSTNAME ":" URI_AUTHORITY_PORT_1 +#define URI_AUTHORITY_LITERAL_HOST6 URI_AUTHORITY_USERINFO "@" URI_AUTHORITY_LITERAL_HOSTNAME6 ":" URI_AUTHORITY_PORT_1 +#define URI_AUTHORITY_LITERAL_HOST6_2 URI_AUTHORITY_USERINFO "@" URI_AUTHORITY_LITERAL_HOSTNAME6_2 ":" URI_AUTHORITY_PORT_1 +#define URI_AUTHORITY_DIFFERENT_USER URI_AUTHORITY_USERINFO_2 "@" URI_AUTHORITY_HOSTNAME ":" URI_AUTHORITY_PORT_1 + +#define URI_PATH_SEGMENT "%00%01%02-.%03%04_%05%06%07%08%09abcdefghijklmnopqrstuvwxyz" +#define URI_PATH_SEGMENT_WITH_SLASHES URI_PATH_SEGMENT "//////" + +#define URI_PATH "/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT + +#define URI_QUERY "x=1&y=2&z=3" + +#define URI_FRAGMENT "alphabetagamma" + +#define URI_FULL URI_SCHEME "://" URI_AUTHORITY "/" URI_PATH "?" URI_QUERY "#" URI_FRAGMENT + +char *TEST_URI_SCHEME = URI_SCHEME; +char *TEST_URI_AUTHORITY = URI_AUTHORITY; +#endif // PARC_Algol__test_parc_URI_h diff --git a/libparc/parc/algol/test/data.json b/libparc/parc/algol/test/data.json new file mode 100644 index 00000000..a146f6ff --- /dev/null +++ b/libparc/parc/algol/test/data.json @@ -0,0 +1,2614 @@ +{ + "array" : [ + { + "id": 6104546, + "name": "-REPONAME", + "full_name": "mralexgray/-REPONAME", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/-REPONAME", + "description": null, + "fork": false, + "url": "https://api.github.com/repos/mralexgray/-REPONAME", + "forks_url": "https://api.github.com/repos/mralexgray/-REPONAME/forks", + "keys_url": "https://api.github.com/repos/mralexgray/-REPONAME/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/-REPONAME/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/-REPONAME/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/-REPONAME/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/-REPONAME/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/-REPONAME/events", + "assignees_url": "https://api.github.com/repos/mralexgray/-REPONAME/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/-REPONAME/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/-REPONAME/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/-REPONAME/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/-REPONAME/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/-REPONAME/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/-REPONAME/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/-REPONAME/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/-REPONAME/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/-REPONAME/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/-REPONAME/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/-REPONAME/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/-REPONAME/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/-REPONAME/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/-REPONAME/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/-REPONAME/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/-REPONAME/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/-REPONAME/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/-REPONAME/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/-REPONAME/merges", + "archive_url": "https://api.github.com/repos/mralexgray/-REPONAME/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/-REPONAME/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/-REPONAME/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/-REPONAME/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/-REPONAME/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/-REPONAME/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/-REPONAME/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/-REPONAME/releases{/id}", + "created_at": "2012-10-06T16:37:39Z", + "updated_at": "2013-01-12T13:39:30Z", + "pushed_at": "2012-10-06T16:37:39Z", + "git_url": "git://github.com/mralexgray/-REPONAME.git", + "ssh_url": "git@github.com:mralexgray/-REPONAME.git", + "clone_url": "https://github.com/mralexgray/-REPONAME.git", + "svn_url": "https://github.com/mralexgray/-REPONAME", + "homepage": null, + "size": 48, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": true, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 13121042, + "name": "ace", + "full_name": "mralexgray/ace", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/ace", + "description": "Ace (Ajax.org Cloud9 Editor)", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/ace", + "forks_url": "https://api.github.com/repos/mralexgray/ace/forks", + "keys_url": "https://api.github.com/repos/mralexgray/ace/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/ace/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/ace/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/ace/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/ace/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/ace/events", + "assignees_url": "https://api.github.com/repos/mralexgray/ace/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/ace/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/ace/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/ace/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/ace/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/ace/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/ace/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/ace/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/ace/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/ace/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/ace/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/ace/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/ace/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/ace/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/ace/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/ace/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/ace/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/ace/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/ace/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/ace/merges", + "archive_url": "https://api.github.com/repos/mralexgray/ace/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/ace/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/ace/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/ace/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/ace/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/ace/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/ace/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/ace/releases{/id}", + "created_at": "2013-09-26T11:58:10Z", + "updated_at": "2013-10-26T12:34:49Z", + "pushed_at": "2013-10-26T12:34:48Z", + "git_url": "git://github.com/mralexgray/ace.git", + "ssh_url": "git@github.com:mralexgray/ace.git", + "clone_url": "https://github.com/mralexgray/ace.git", + "svn_url": "https://github.com/mralexgray/ace", + "homepage": "http://ace.c9.io", + "size": 21080, + "stargazers_count": 0, + "watchers_count": 0, + "language": "JavaScript", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 10791045, + "name": "ACEView", + "full_name": "mralexgray/ACEView", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/ACEView", + "description": "Use the wonderful ACE editor in your Cocoa applications", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/ACEView", + "forks_url": "https://api.github.com/repos/mralexgray/ACEView/forks", + "keys_url": "https://api.github.com/repos/mralexgray/ACEView/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/ACEView/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/ACEView/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/ACEView/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/ACEView/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/ACEView/events", + "assignees_url": "https://api.github.com/repos/mralexgray/ACEView/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/ACEView/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/ACEView/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/ACEView/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/ACEView/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/ACEView/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/ACEView/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/ACEView/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/ACEView/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/ACEView/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/ACEView/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/ACEView/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/ACEView/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/ACEView/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/ACEView/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/ACEView/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/ACEView/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/ACEView/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/ACEView/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/ACEView/merges", + "archive_url": "https://api.github.com/repos/mralexgray/ACEView/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/ACEView/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/ACEView/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/ACEView/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/ACEView/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/ACEView/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/ACEView/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/ACEView/releases{/id}", + "created_at": "2013-06-19T12:15:04Z", + "updated_at": "2013-10-30T12:39:24Z", + "pushed_at": "2013-10-30T12:39:18Z", + "git_url": "git://github.com/mralexgray/ACEView.git", + "ssh_url": "git@github.com:mralexgray/ACEView.git", + "clone_url": "https://github.com/mralexgray/ACEView.git", + "svn_url": "https://github.com/mralexgray/ACEView", + "homepage": null, + "size": 1661, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 13623648, + "name": "ActiveLog", + "full_name": "mralexgray/ActiveLog", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/ActiveLog", + "description": "Shut up all logs with active filter.", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/ActiveLog", + "forks_url": "https://api.github.com/repos/mralexgray/ActiveLog/forks", + "keys_url": "https://api.github.com/repos/mralexgray/ActiveLog/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/ActiveLog/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/ActiveLog/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/ActiveLog/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/ActiveLog/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/ActiveLog/events", + "assignees_url": "https://api.github.com/repos/mralexgray/ActiveLog/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/ActiveLog/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/ActiveLog/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/ActiveLog/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/ActiveLog/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/ActiveLog/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/ActiveLog/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/ActiveLog/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/ActiveLog/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/ActiveLog/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/ActiveLog/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/ActiveLog/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/ActiveLog/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/ActiveLog/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/ActiveLog/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/ActiveLog/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/ActiveLog/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/ActiveLog/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/ActiveLog/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/ActiveLog/merges", + "archive_url": "https://api.github.com/repos/mralexgray/ActiveLog/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/ActiveLog/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/ActiveLog/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/ActiveLog/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/ActiveLog/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/ActiveLog/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/ActiveLog/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/ActiveLog/releases{/id}", + "created_at": "2013-10-16T15:52:37Z", + "updated_at": "2013-10-16T15:52:37Z", + "pushed_at": "2011-07-03T06:28:59Z", + "git_url": "git://github.com/mralexgray/ActiveLog.git", + "ssh_url": "git@github.com:mralexgray/ActiveLog.git", + "clone_url": "https://github.com/mralexgray/ActiveLog.git", + "svn_url": "https://github.com/mralexgray/ActiveLog", + "homepage": "http://deepitpro.com/en/articles/ActiveLog/info/", + "size": 60, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 9716210, + "name": "adium", + "full_name": "mralexgray/adium", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/adium", + "description": "Official mirror of hg.adium.im", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/adium", + "forks_url": "https://api.github.com/repos/mralexgray/adium/forks", + "keys_url": "https://api.github.com/repos/mralexgray/adium/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/adium/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/adium/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/adium/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/adium/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/adium/events", + "assignees_url": "https://api.github.com/repos/mralexgray/adium/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/adium/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/adium/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/adium/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/adium/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/adium/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/adium/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/adium/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/adium/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/adium/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/adium/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/adium/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/adium/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/adium/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/adium/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/adium/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/adium/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/adium/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/adium/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/adium/merges", + "archive_url": "https://api.github.com/repos/mralexgray/adium/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/adium/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/adium/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/adium/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/adium/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/adium/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/adium/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/adium/releases{/id}", + "created_at": "2013-04-27T14:59:33Z", + "updated_at": "2013-04-27T14:59:33Z", + "pushed_at": "2013-04-26T16:43:53Z", + "git_url": "git://github.com/mralexgray/adium.git", + "ssh_url": "git@github.com:mralexgray/adium.git", + "clone_url": "https://github.com/mralexgray/adium.git", + "svn_url": "https://github.com/mralexgray/adium", + "homepage": null, + "size": 277719, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": false, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 12752329, + "name": "ADLivelyTableView", + "full_name": "mralexgray/ADLivelyTableView", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/ADLivelyTableView", + "description": "Lively UITableView", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/ADLivelyTableView", + "forks_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/forks", + "keys_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/events", + "assignees_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/merges", + "archive_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/releases{/id}", + "created_at": "2013-09-11T09:18:01Z", + "updated_at": "2013-09-11T09:18:03Z", + "pushed_at": "2012-05-10T10:40:15Z", + "git_url": "git://github.com/mralexgray/ADLivelyTableView.git", + "ssh_url": "git@github.com:mralexgray/ADLivelyTableView.git", + "clone_url": "https://github.com/mralexgray/ADLivelyTableView.git", + "svn_url": "https://github.com/mralexgray/ADLivelyTableView", + "homepage": "http://applidium.com/en/news/lively_uitableview/", + "size": 73, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 5697379, + "name": "AFIncrementalStore", + "full_name": "mralexgray/AFIncrementalStore", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/AFIncrementalStore", + "description": "Core Data Persistence with AFNetworking, Done Right", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/AFIncrementalStore", + "forks_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/forks", + "keys_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/events", + "assignees_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/merges", + "archive_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/releases{/id}", + "created_at": "2012-09-06T04:20:33Z", + "updated_at": "2013-01-12T03:15:29Z", + "pushed_at": "2012-09-01T22:46:25Z", + "git_url": "git://github.com/mralexgray/AFIncrementalStore.git", + "ssh_url": "git@github.com:mralexgray/AFIncrementalStore.git", + "clone_url": "https://github.com/mralexgray/AFIncrementalStore.git", + "svn_url": "https://github.com/mralexgray/AFIncrementalStore", + "homepage": null, + "size": 139, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 6969621, + "name": "AFNetworking", + "full_name": "mralexgray/AFNetworking", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/AFNetworking", + "description": "A delightful iOS and OS X networking framework", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/AFNetworking", + "forks_url": "https://api.github.com/repos/mralexgray/AFNetworking/forks", + "keys_url": "https://api.github.com/repos/mralexgray/AFNetworking/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/AFNetworking/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/AFNetworking/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/AFNetworking/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/AFNetworking/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/AFNetworking/events", + "assignees_url": "https://api.github.com/repos/mralexgray/AFNetworking/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/AFNetworking/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/AFNetworking/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/AFNetworking/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/AFNetworking/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/AFNetworking/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/AFNetworking/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/AFNetworking/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/AFNetworking/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/AFNetworking/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/AFNetworking/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/AFNetworking/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/AFNetworking/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/AFNetworking/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/AFNetworking/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/AFNetworking/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/AFNetworking/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/AFNetworking/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/AFNetworking/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/AFNetworking/merges", + "archive_url": "https://api.github.com/repos/mralexgray/AFNetworking/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/AFNetworking/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/AFNetworking/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/AFNetworking/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/AFNetworking/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/AFNetworking/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/AFNetworking/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/AFNetworking/releases{/id}", + "created_at": "2012-12-02T17:00:04Z", + "updated_at": "2014-01-24T07:14:33Z", + "pushed_at": "2014-01-24T07:14:32Z", + "git_url": "git://github.com/mralexgray/AFNetworking.git", + "ssh_url": "git@github.com:mralexgray/AFNetworking.git", + "clone_url": "https://github.com/mralexgray/AFNetworking.git", + "svn_url": "https://github.com/mralexgray/AFNetworking", + "homepage": "http://afnetworking.com", + "size": 4341, + "stargazers_count": 1, + "watchers_count": 1, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 1, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 9485541, + "name": "AGNSSplitView", + "full_name": "mralexgray/AGNSSplitView", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/AGNSSplitView", + "description": "Simple NSSplitView additions.", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/AGNSSplitView", + "forks_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/forks", + "keys_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/events", + "assignees_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/merges", + "archive_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/releases{/id}", + "created_at": "2013-04-17T00:10:13Z", + "updated_at": "2013-04-17T00:10:13Z", + "pushed_at": "2013-02-26T00:32:32Z", + "git_url": "git://github.com/mralexgray/AGNSSplitView.git", + "ssh_url": "git@github.com:mralexgray/AGNSSplitView.git", + "clone_url": "https://github.com/mralexgray/AGNSSplitView.git", + "svn_url": "https://github.com/mralexgray/AGNSSplitView", + "homepage": null, + "size": 68, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 12767784, + "name": "AGScopeBar", + "full_name": "mralexgray/AGScopeBar", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/AGScopeBar", + "description": "Custom scope bar implementation for Cocoa", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/AGScopeBar", + "forks_url": "https://api.github.com/repos/mralexgray/AGScopeBar/forks", + "keys_url": "https://api.github.com/repos/mralexgray/AGScopeBar/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/AGScopeBar/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/AGScopeBar/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/AGScopeBar/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/AGScopeBar/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/AGScopeBar/events", + "assignees_url": "https://api.github.com/repos/mralexgray/AGScopeBar/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/AGScopeBar/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/AGScopeBar/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/AGScopeBar/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/AGScopeBar/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/AGScopeBar/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/AGScopeBar/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/AGScopeBar/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/AGScopeBar/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/AGScopeBar/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/AGScopeBar/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/AGScopeBar/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/AGScopeBar/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/AGScopeBar/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/AGScopeBar/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/AGScopeBar/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/AGScopeBar/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/AGScopeBar/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/AGScopeBar/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/AGScopeBar/merges", + "archive_url": "https://api.github.com/repos/mralexgray/AGScopeBar/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/AGScopeBar/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/AGScopeBar/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/AGScopeBar/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/AGScopeBar/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/AGScopeBar/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/AGScopeBar/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/AGScopeBar/releases{/id}", + "created_at": "2013-09-11T21:06:54Z", + "updated_at": "2013-09-11T21:06:54Z", + "pushed_at": "2013-05-07T03:35:29Z", + "git_url": "git://github.com/mralexgray/AGScopeBar.git", + "ssh_url": "git@github.com:mralexgray/AGScopeBar.git", + "clone_url": "https://github.com/mralexgray/AGScopeBar.git", + "svn_url": "https://github.com/mralexgray/AGScopeBar", + "homepage": null, + "size": 64, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 9227846, + "name": "AHContentBrowser", + "full_name": "mralexgray/AHContentBrowser", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/AHContentBrowser", + "description": "A Mac only webview that loads a fast readable version of the website if available.", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/AHContentBrowser", + "forks_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/forks", + "keys_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/events", + "assignees_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/merges", + "archive_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/releases{/id}", + "created_at": "2013-04-04T20:56:16Z", + "updated_at": "2013-04-04T20:56:16Z", + "pushed_at": "2013-03-13T17:38:23Z", + "git_url": "git://github.com/mralexgray/AHContentBrowser.git", + "ssh_url": "git@github.com:mralexgray/AHContentBrowser.git", + "clone_url": "https://github.com/mralexgray/AHContentBrowser.git", + "svn_url": "https://github.com/mralexgray/AHContentBrowser", + "homepage": "", + "size": 223, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 9167473, + "name": "AHLayout", + "full_name": "mralexgray/AHLayout", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/AHLayout", + "description": "AHLayout", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/AHLayout", + "forks_url": "https://api.github.com/repos/mralexgray/AHLayout/forks", + "keys_url": "https://api.github.com/repos/mralexgray/AHLayout/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/AHLayout/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/AHLayout/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/AHLayout/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/AHLayout/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/AHLayout/events", + "assignees_url": "https://api.github.com/repos/mralexgray/AHLayout/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/AHLayout/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/AHLayout/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/AHLayout/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/AHLayout/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/AHLayout/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/AHLayout/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/AHLayout/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/AHLayout/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/AHLayout/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/AHLayout/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/AHLayout/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/AHLayout/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/AHLayout/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/AHLayout/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/AHLayout/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/AHLayout/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/AHLayout/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/AHLayout/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/AHLayout/merges", + "archive_url": "https://api.github.com/repos/mralexgray/AHLayout/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/AHLayout/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/AHLayout/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/AHLayout/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/AHLayout/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/AHLayout/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/AHLayout/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/AHLayout/releases{/id}", + "created_at": "2013-04-02T10:10:30Z", + "updated_at": "2013-07-08T02:31:17Z", + "pushed_at": "2013-07-08T02:31:14Z", + "git_url": "git://github.com/mralexgray/AHLayout.git", + "ssh_url": "git@github.com:mralexgray/AHLayout.git", + "clone_url": "https://github.com/mralexgray/AHLayout.git", + "svn_url": "https://github.com/mralexgray/AHLayout", + "homepage": null, + "size": 359, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 18450201, + "name": "Airmail-Plug-In-Framework", + "full_name": "mralexgray/Airmail-Plug-In-Framework", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/Airmail-Plug-In-Framework", + "description": "", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework", + "forks_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/forks", + "keys_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/events", + "assignees_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/merges", + "archive_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/releases{/id}", + "created_at": "2014-04-04T19:33:54Z", + "updated_at": "2014-04-04T19:33:54Z", + "pushed_at": "2014-03-27T15:42:19Z", + "git_url": "git://github.com/mralexgray/Airmail-Plug-In-Framework.git", + "ssh_url": "git@github.com:mralexgray/Airmail-Plug-In-Framework.git", + "clone_url": "https://github.com/mralexgray/Airmail-Plug-In-Framework.git", + "svn_url": "https://github.com/mralexgray/Airmail-Plug-In-Framework", + "homepage": null, + "size": 888, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 5203219, + "name": "AJS-iTunes-API", + "full_name": "mralexgray/AJS-iTunes-API", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/AJS-iTunes-API", + "description": "Cocoa wrapper for the iTunes search API - for iOS and Mac OSX projects", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API", + "forks_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/forks", + "keys_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/events", + "assignees_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/merges", + "archive_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/releases{/id}", + "created_at": "2012-07-27T10:20:58Z", + "updated_at": "2013-01-11T11:00:05Z", + "pushed_at": "2011-10-30T22:26:48Z", + "git_url": "git://github.com/mralexgray/AJS-iTunes-API.git", + "ssh_url": "git@github.com:mralexgray/AJS-iTunes-API.git", + "clone_url": "https://github.com/mralexgray/AJS-iTunes-API.git", + "svn_url": "https://github.com/mralexgray/AJS-iTunes-API", + "homepage": "", + "size": 103, + "stargazers_count": 1, + "watchers_count": 1, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 1, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 10093801, + "name": "Alcatraz", + "full_name": "mralexgray/Alcatraz", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/Alcatraz", + "description": "The most awesome (and only) Xcode package manager!", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/Alcatraz", + "forks_url": "https://api.github.com/repos/mralexgray/Alcatraz/forks", + "keys_url": "https://api.github.com/repos/mralexgray/Alcatraz/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/Alcatraz/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/Alcatraz/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/Alcatraz/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/Alcatraz/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/Alcatraz/events", + "assignees_url": "https://api.github.com/repos/mralexgray/Alcatraz/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/Alcatraz/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/Alcatraz/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/Alcatraz/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/Alcatraz/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/Alcatraz/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/Alcatraz/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/Alcatraz/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/Alcatraz/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/Alcatraz/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/Alcatraz/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/Alcatraz/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/Alcatraz/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/Alcatraz/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/Alcatraz/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/Alcatraz/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/Alcatraz/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/Alcatraz/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/Alcatraz/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/Alcatraz/merges", + "archive_url": "https://api.github.com/repos/mralexgray/Alcatraz/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/Alcatraz/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/Alcatraz/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/Alcatraz/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/Alcatraz/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/Alcatraz/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/Alcatraz/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/Alcatraz/releases{/id}", + "created_at": "2013-05-16T04:41:13Z", + "updated_at": "2014-03-19T20:38:35Z", + "pushed_at": "2014-03-19T12:50:37Z", + "git_url": "git://github.com/mralexgray/Alcatraz.git", + "ssh_url": "git@github.com:mralexgray/Alcatraz.git", + "clone_url": "https://github.com/mralexgray/Alcatraz.git", + "svn_url": "https://github.com/mralexgray/Alcatraz", + "homepage": "mneorr.github.com/Alcatraz", + "size": 3668, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": false, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 12916552, + "name": "alcatraz-packages", + "full_name": "mralexgray/alcatraz-packages", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/alcatraz-packages", + "description": "Package list repository for Alcatraz", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/alcatraz-packages", + "forks_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/forks", + "keys_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/events", + "assignees_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/merges", + "archive_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/releases{/id}", + "created_at": "2013-09-18T07:15:24Z", + "updated_at": "2013-09-18T07:15:25Z", + "pushed_at": "2013-09-09T07:51:48Z", + "git_url": "git://github.com/mralexgray/alcatraz-packages.git", + "ssh_url": "git@github.com:mralexgray/alcatraz-packages.git", + "clone_url": "https://github.com/mralexgray/alcatraz-packages.git", + "svn_url": "https://github.com/mralexgray/alcatraz-packages", + "homepage": "mneorr.github.com/Alcatraz", + "size": 482, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Ruby", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 10476467, + "name": "Alfred-Google-Translate", + "full_name": "mralexgray/Alfred-Google-Translate", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/Alfred-Google-Translate", + "description": "Extension for Alfred that will do a Google translate for you", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate", + "forks_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/forks", + "keys_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/events", + "assignees_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/merges", + "archive_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/releases{/id}", + "created_at": "2013-06-04T10:45:10Z", + "updated_at": "2013-06-04T10:45:10Z", + "pushed_at": "2013-01-12T19:39:03Z", + "git_url": "git://github.com/mralexgray/Alfred-Google-Translate.git", + "ssh_url": "git@github.com:mralexgray/Alfred-Google-Translate.git", + "clone_url": "https://github.com/mralexgray/Alfred-Google-Translate.git", + "svn_url": "https://github.com/mralexgray/Alfred-Google-Translate", + "homepage": null, + "size": 103, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Shell", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 5524019, + "name": "Amber", + "full_name": "mralexgray/Amber", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/Amber", + "description": "Fork of the difficult-to-deal-with Amber.framework", + "fork": false, + "url": "https://api.github.com/repos/mralexgray/Amber", + "forks_url": "https://api.github.com/repos/mralexgray/Amber/forks", + "keys_url": "https://api.github.com/repos/mralexgray/Amber/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/Amber/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/Amber/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/Amber/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/Amber/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/Amber/events", + "assignees_url": "https://api.github.com/repos/mralexgray/Amber/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/Amber/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/Amber/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/Amber/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/Amber/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/Amber/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/Amber/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/Amber/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/Amber/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/Amber/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/Amber/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/Amber/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/Amber/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/Amber/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/Amber/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/Amber/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/Amber/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/Amber/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/Amber/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/Amber/merges", + "archive_url": "https://api.github.com/repos/mralexgray/Amber/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/Amber/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/Amber/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/Amber/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/Amber/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/Amber/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/Amber/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/Amber/releases{/id}", + "created_at": "2012-08-23T10:38:24Z", + "updated_at": "2013-01-11T22:25:35Z", + "pushed_at": "2012-08-23T10:38:25Z", + "git_url": "git://github.com/mralexgray/Amber.git", + "ssh_url": "git@github.com:mralexgray/Amber.git", + "clone_url": "https://github.com/mralexgray/Amber.git", + "svn_url": "https://github.com/mralexgray/Amber", + "homepage": null, + "size": 48, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": true, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 10809060, + "name": "Amethyst", + "full_name": "mralexgray/Amethyst", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/Amethyst", + "description": "Tiling window manager for OS X.", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/Amethyst", + "forks_url": "https://api.github.com/repos/mralexgray/Amethyst/forks", + "keys_url": "https://api.github.com/repos/mralexgray/Amethyst/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/Amethyst/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/Amethyst/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/Amethyst/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/Amethyst/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/Amethyst/events", + "assignees_url": "https://api.github.com/repos/mralexgray/Amethyst/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/Amethyst/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/Amethyst/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/Amethyst/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/Amethyst/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/Amethyst/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/Amethyst/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/Amethyst/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/Amethyst/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/Amethyst/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/Amethyst/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/Amethyst/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/Amethyst/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/Amethyst/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/Amethyst/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/Amethyst/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/Amethyst/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/Amethyst/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/Amethyst/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/Amethyst/merges", + "archive_url": "https://api.github.com/repos/mralexgray/Amethyst/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/Amethyst/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/Amethyst/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/Amethyst/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/Amethyst/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/Amethyst/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/Amethyst/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/Amethyst/releases{/id}", + "created_at": "2013-06-20T00:34:22Z", + "updated_at": "2013-06-20T00:34:22Z", + "pushed_at": "2013-06-18T02:54:11Z", + "git_url": "git://github.com/mralexgray/Amethyst.git", + "ssh_url": "git@github.com:mralexgray/Amethyst.git", + "clone_url": "https://github.com/mralexgray/Amethyst.git", + "svn_url": "https://github.com/mralexgray/Amethyst", + "homepage": "http://ianyh.github.io/Amethyst/", + "size": 12623, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 3684286, + "name": "Animated-Paths", + "full_name": "mralexgray/Animated-Paths", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/Animated-Paths", + "description": "Demo project: Animating the drawing of a CGPath with CAShapeLayer.strokeEnd", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/Animated-Paths", + "forks_url": "https://api.github.com/repos/mralexgray/Animated-Paths/forks", + "keys_url": "https://api.github.com/repos/mralexgray/Animated-Paths/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/Animated-Paths/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/Animated-Paths/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/Animated-Paths/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/Animated-Paths/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/Animated-Paths/events", + "assignees_url": "https://api.github.com/repos/mralexgray/Animated-Paths/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/Animated-Paths/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/Animated-Paths/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/Animated-Paths/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/Animated-Paths/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/Animated-Paths/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/Animated-Paths/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/Animated-Paths/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/Animated-Paths/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/Animated-Paths/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/Animated-Paths/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/Animated-Paths/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/Animated-Paths/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/Animated-Paths/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/Animated-Paths/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/Animated-Paths/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/Animated-Paths/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/Animated-Paths/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/Animated-Paths/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/Animated-Paths/merges", + "archive_url": "https://api.github.com/repos/mralexgray/Animated-Paths/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/Animated-Paths/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/Animated-Paths/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/Animated-Paths/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/Animated-Paths/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/Animated-Paths/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/Animated-Paths/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/Animated-Paths/releases{/id}", + "created_at": "2012-03-11T02:56:38Z", + "updated_at": "2013-01-08T04:12:21Z", + "pushed_at": "2010-12-30T20:56:51Z", + "git_url": "git://github.com/mralexgray/Animated-Paths.git", + "ssh_url": "git@github.com:mralexgray/Animated-Paths.git", + "clone_url": "https://github.com/mralexgray/Animated-Paths.git", + "svn_url": "https://github.com/mralexgray/Animated-Paths", + "homepage": "http://oleb.net/blog/2010/12/animating-drawing-of-cgpath-with-cashapelayer/", + "size": 411, + "stargazers_count": 1, + "watchers_count": 1, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 1, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 16662874, + "name": "AnsiLove.framework", + "full_name": "mralexgray/AnsiLove.framework", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/AnsiLove.framework", + "description": "Cocoa Framework for rendering ANSi / ASCII art", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/AnsiLove.framework", + "forks_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/forks", + "keys_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/events", + "assignees_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/merges", + "archive_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/releases{/id}", + "created_at": "2014-02-09T08:30:27Z", + "updated_at": "2014-02-09T08:30:32Z", + "pushed_at": "2013-10-04T14:08:38Z", + "git_url": "git://github.com/mralexgray/AnsiLove.framework.git", + "ssh_url": "git@github.com:mralexgray/AnsiLove.framework.git", + "clone_url": "https://github.com/mralexgray/AnsiLove.framework.git", + "svn_url": "https://github.com/mralexgray/AnsiLove.framework", + "homepage": "http://byteproject.net", + "size": 3780, + "stargazers_count": 0, + "watchers_count": 0, + "language": "M", + "has_issues": false, + "has_downloads": true, + "has_wiki": false, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 5189563, + "name": "ANTrackBar", + "full_name": "mralexgray/ANTrackBar", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/ANTrackBar", + "description": "An easy-to-use Cocoa seek bar with a pleasing appearance", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/ANTrackBar", + "forks_url": "https://api.github.com/repos/mralexgray/ANTrackBar/forks", + "keys_url": "https://api.github.com/repos/mralexgray/ANTrackBar/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/ANTrackBar/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/ANTrackBar/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/ANTrackBar/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/ANTrackBar/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/ANTrackBar/events", + "assignees_url": "https://api.github.com/repos/mralexgray/ANTrackBar/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/ANTrackBar/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/ANTrackBar/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/ANTrackBar/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/ANTrackBar/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/ANTrackBar/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/ANTrackBar/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/ANTrackBar/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/ANTrackBar/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/ANTrackBar/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/ANTrackBar/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/ANTrackBar/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/ANTrackBar/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/ANTrackBar/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/ANTrackBar/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/ANTrackBar/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/ANTrackBar/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/ANTrackBar/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/ANTrackBar/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/ANTrackBar/merges", + "archive_url": "https://api.github.com/repos/mralexgray/ANTrackBar/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/ANTrackBar/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/ANTrackBar/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/ANTrackBar/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/ANTrackBar/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/ANTrackBar/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/ANTrackBar/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/ANTrackBar/releases{/id}", + "created_at": "2012-07-26T08:17:22Z", + "updated_at": "2013-01-11T10:29:56Z", + "pushed_at": "2012-03-09T01:40:02Z", + "git_url": "git://github.com/mralexgray/ANTrackBar.git", + "ssh_url": "git@github.com:mralexgray/ANTrackBar.git", + "clone_url": "https://github.com/mralexgray/ANTrackBar.git", + "svn_url": "https://github.com/mralexgray/ANTrackBar", + "homepage": "", + "size": 94, + "stargazers_count": 1, + "watchers_count": 1, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 1, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 16240152, + "name": "AOP-in-Objective-C", + "full_name": "mralexgray/AOP-in-Objective-C", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/AOP-in-Objective-C", + "description": "An NSProxy based library for easily enabling AOP like functionality in Objective-C.", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C", + "forks_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/forks", + "keys_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/events", + "assignees_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/merges", + "archive_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/releases{/id}", + "created_at": "2014-01-25T21:18:04Z", + "updated_at": "2014-02-12T16:23:21Z", + "pushed_at": "2014-02-12T16:23:20Z", + "git_url": "git://github.com/mralexgray/AOP-in-Objective-C.git", + "ssh_url": "git@github.com:mralexgray/AOP-in-Objective-C.git", + "clone_url": "https://github.com/mralexgray/AOP-in-Objective-C.git", + "svn_url": "https://github.com/mralexgray/AOP-in-Objective-C", + "homepage": "http://innoli.hu/en/opensource/", + "size": 340, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 1, + "mirror_url": null, + "open_issues_count": 0, + "forks": 1, + "open_issues": 0, + "watchers": 0, + "default_branch": "travis-coveralls", + "master_branch": "travis-coveralls" + }, + { + "id": 13141936, + "name": "Apaxy", + "full_name": "mralexgray/Apaxy", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/Apaxy", + "description": "A simple, customisable theme for your Apache directory listing.", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/Apaxy", + "forks_url": "https://api.github.com/repos/mralexgray/Apaxy/forks", + "keys_url": "https://api.github.com/repos/mralexgray/Apaxy/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/Apaxy/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/Apaxy/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/Apaxy/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/Apaxy/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/Apaxy/events", + "assignees_url": "https://api.github.com/repos/mralexgray/Apaxy/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/Apaxy/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/Apaxy/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/Apaxy/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/Apaxy/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/Apaxy/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/Apaxy/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/Apaxy/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/Apaxy/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/Apaxy/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/Apaxy/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/Apaxy/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/Apaxy/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/Apaxy/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/Apaxy/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/Apaxy/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/Apaxy/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/Apaxy/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/Apaxy/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/Apaxy/merges", + "archive_url": "https://api.github.com/repos/mralexgray/Apaxy/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/Apaxy/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/Apaxy/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/Apaxy/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/Apaxy/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/Apaxy/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/Apaxy/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/Apaxy/releases{/id}", + "created_at": "2013-09-27T05:05:35Z", + "updated_at": "2013-09-27T05:05:36Z", + "pushed_at": "2013-08-02T16:01:32Z", + "git_url": "git://github.com/mralexgray/Apaxy.git", + "ssh_url": "git@github.com:mralexgray/Apaxy.git", + "clone_url": "https://github.com/mralexgray/Apaxy.git", + "svn_url": "https://github.com/mralexgray/Apaxy", + "homepage": null, + "size": 113, + "stargazers_count": 0, + "watchers_count": 0, + "language": "CSS", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 10048098, + "name": "appledoc", + "full_name": "mralexgray/appledoc", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/appledoc", + "description": "Objective-c code Apple style documentation set generator.", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/appledoc", + "forks_url": "https://api.github.com/repos/mralexgray/appledoc/forks", + "keys_url": "https://api.github.com/repos/mralexgray/appledoc/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/appledoc/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/appledoc/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/appledoc/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/appledoc/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/appledoc/events", + "assignees_url": "https://api.github.com/repos/mralexgray/appledoc/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/appledoc/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/appledoc/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/appledoc/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/appledoc/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/appledoc/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/appledoc/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/appledoc/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/appledoc/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/appledoc/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/appledoc/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/appledoc/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/appledoc/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/appledoc/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/appledoc/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/appledoc/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/appledoc/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/appledoc/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/appledoc/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/appledoc/merges", + "archive_url": "https://api.github.com/repos/mralexgray/appledoc/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/appledoc/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/appledoc/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/appledoc/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/appledoc/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/appledoc/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/appledoc/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/appledoc/releases{/id}", + "created_at": "2013-05-14T05:45:44Z", + "updated_at": "2014-02-09T08:14:42Z", + "pushed_at": "2014-02-09T08:14:42Z", + "git_url": "git://github.com/mralexgray/appledoc.git", + "ssh_url": "git@github.com:mralexgray/appledoc.git", + "clone_url": "https://github.com/mralexgray/appledoc.git", + "svn_url": "https://github.com/mralexgray/appledoc", + "homepage": "http://gentlebytes.com", + "size": 10336, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Objective-C", + "has_issues": false, + "has_downloads": false, + "has_wiki": false, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 16160992, + "name": "Appstore-Through-Terminal", + "full_name": "mralexgray/Appstore-Through-Terminal", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/Appstore-Through-Terminal", + "description": "A simple debian package, made for iPhone, to open the iOS AppStore through terminal. Simple. Slightly usless. My work as a beginner.", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal", + "forks_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/forks", + "keys_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/events", + "assignees_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/merges", + "archive_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/releases{/id}", + "created_at": "2014-01-23T03:21:34Z", + "updated_at": "2014-01-23T03:21:34Z", + "pushed_at": "2011-12-21T06:40:25Z", + "git_url": "git://github.com/mralexgray/Appstore-Through-Terminal.git", + "ssh_url": "git@github.com:mralexgray/Appstore-Through-Terminal.git", + "clone_url": "https://github.com/mralexgray/Appstore-Through-Terminal.git", + "svn_url": "https://github.com/mralexgray/Appstore-Through-Terminal", + "homepage": "http://getagripon.com/tillie/projects.html", + "size": 104, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Shell", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 13709216, + "name": "appweb-4", + "full_name": "mralexgray/appweb-4", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/appweb-4", + "description": "Appweb Embeddable Web Server 4", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/appweb-4", + "forks_url": "https://api.github.com/repos/mralexgray/appweb-4/forks", + "keys_url": "https://api.github.com/repos/mralexgray/appweb-4/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/appweb-4/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/appweb-4/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/appweb-4/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/appweb-4/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/appweb-4/events", + "assignees_url": "https://api.github.com/repos/mralexgray/appweb-4/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/appweb-4/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/appweb-4/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/appweb-4/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/appweb-4/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/appweb-4/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/appweb-4/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/appweb-4/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/appweb-4/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/appweb-4/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/appweb-4/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/appweb-4/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/appweb-4/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/appweb-4/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/appweb-4/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/appweb-4/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/appweb-4/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/appweb-4/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/appweb-4/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/appweb-4/merges", + "archive_url": "https://api.github.com/repos/mralexgray/appweb-4/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/appweb-4/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/appweb-4/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/appweb-4/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/appweb-4/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/appweb-4/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/appweb-4/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/appweb-4/releases{/id}", + "created_at": "2013-10-19T21:36:10Z", + "updated_at": "2013-10-19T21:36:11Z", + "pushed_at": "2013-10-19T00:35:06Z", + "git_url": "git://github.com/mralexgray/appweb-4.git", + "ssh_url": "git@github.com:mralexgray/appweb-4.git", + "clone_url": "https://github.com/mralexgray/appweb-4.git", + "svn_url": "https://github.com/mralexgray/appweb-4", + "homepage": "http://appwebserver.org", + "size": 58244, + "stargazers_count": 0, + "watchers_count": 0, + "language": "C", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 12501983, + "name": "arbor", + "full_name": "mralexgray/arbor", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/arbor", + "description": "a graph visualization library using web workers and jQuery", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/arbor", + "forks_url": "https://api.github.com/repos/mralexgray/arbor/forks", + "keys_url": "https://api.github.com/repos/mralexgray/arbor/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/arbor/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/arbor/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/arbor/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/arbor/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/arbor/events", + "assignees_url": "https://api.github.com/repos/mralexgray/arbor/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/arbor/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/arbor/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/arbor/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/arbor/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/arbor/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/arbor/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/arbor/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/arbor/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/arbor/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/arbor/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/arbor/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/arbor/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/arbor/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/arbor/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/arbor/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/arbor/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/arbor/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/arbor/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/arbor/merges", + "archive_url": "https://api.github.com/repos/mralexgray/arbor/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/arbor/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/arbor/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/arbor/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/arbor/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/arbor/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/arbor/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/arbor/releases{/id}", + "created_at": "2013-08-31T07:07:05Z", + "updated_at": "2013-08-31T07:07:06Z", + "pushed_at": "2012-05-28T00:47:58Z", + "git_url": "git://github.com/mralexgray/arbor.git", + "ssh_url": "git@github.com:mralexgray/arbor.git", + "clone_url": "https://github.com/mralexgray/arbor.git", + "svn_url": "https://github.com/mralexgray/arbor", + "homepage": "http://arborjs.org", + "size": 237, + "stargazers_count": 0, + "watchers_count": 0, + "language": "JavaScript", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 13537888, + "name": "Archimedes", + "full_name": "mralexgray/Archimedes", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/Archimedes", + "description": "Geometry functions for Cocoa and Cocoa Touch", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/Archimedes", + "forks_url": "https://api.github.com/repos/mralexgray/Archimedes/forks", + "keys_url": "https://api.github.com/repos/mralexgray/Archimedes/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/Archimedes/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/Archimedes/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/Archimedes/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/Archimedes/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/Archimedes/events", + "assignees_url": "https://api.github.com/repos/mralexgray/Archimedes/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/Archimedes/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/Archimedes/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/Archimedes/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/Archimedes/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/Archimedes/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/Archimedes/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/Archimedes/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/Archimedes/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/Archimedes/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/Archimedes/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/Archimedes/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/Archimedes/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/Archimedes/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/Archimedes/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/Archimedes/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/Archimedes/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/Archimedes/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/Archimedes/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/Archimedes/merges", + "archive_url": "https://api.github.com/repos/mralexgray/Archimedes/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/Archimedes/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/Archimedes/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/Archimedes/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/Archimedes/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/Archimedes/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/Archimedes/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/Archimedes/releases{/id}", + "created_at": "2013-10-13T11:08:19Z", + "updated_at": "2014-04-06T00:41:21Z", + "pushed_at": "2014-04-06T00:41:20Z", + "git_url": "git://github.com/mralexgray/Archimedes.git", + "ssh_url": "git@github.com:mralexgray/Archimedes.git", + "clone_url": "https://github.com/mralexgray/Archimedes.git", + "svn_url": "https://github.com/mralexgray/Archimedes", + "homepage": null, + "size": 217, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 5260205, + "name": "arrsync", + "full_name": "mralexgray/arrsync", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/arrsync", + "description": "", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/arrsync", + "forks_url": "https://api.github.com/repos/mralexgray/arrsync/forks", + "keys_url": "https://api.github.com/repos/mralexgray/arrsync/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/arrsync/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/arrsync/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/arrsync/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/arrsync/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/arrsync/events", + "assignees_url": "https://api.github.com/repos/mralexgray/arrsync/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/arrsync/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/arrsync/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/arrsync/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/arrsync/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/arrsync/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/arrsync/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/arrsync/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/arrsync/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/arrsync/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/arrsync/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/arrsync/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/arrsync/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/arrsync/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/arrsync/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/arrsync/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/arrsync/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/arrsync/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/arrsync/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/arrsync/merges", + "archive_url": "https://api.github.com/repos/mralexgray/arrsync/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/arrsync/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/arrsync/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/arrsync/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/arrsync/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/arrsync/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/arrsync/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/arrsync/releases{/id}", + "created_at": "2012-08-01T14:21:49Z", + "updated_at": "2013-01-11T13:12:21Z", + "pushed_at": "2011-05-09T18:56:56Z", + "git_url": "git://github.com/mralexgray/arrsync.git", + "ssh_url": "git@github.com:mralexgray/arrsync.git", + "clone_url": "https://github.com/mralexgray/arrsync.git", + "svn_url": "https://github.com/mralexgray/arrsync", + "homepage": "", + "size": 194, + "stargazers_count": 2, + "watchers_count": 2, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 2, + "default_branch": "master", + "master_branch": "master" + } +] +}
\ No newline at end of file diff --git a/libparc/parc/algol/test/test_parc_ArrayList.c b/libparc/parc/algol/test/test_parc_ArrayList.c new file mode 100755 index 00000000..9e387a28 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_ArrayList.c @@ -0,0 +1,658 @@ +/* + * 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 "../parc_ArrayList.c" +#include <LongBow/unit-test.h> + +#include <stdio.h> +#include <string.h> + +#include <parc/algol/parc_SafeMemory.h> +#include <parc/testing/parc_ObjectTesting.h> + +#include <parc/algol/parc_Buffer.h> + +LONGBOW_TEST_RUNNER(PARC_ArrayList) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); + LONGBOW_RUN_TEST_FIXTURE(Errors); +} + +LONGBOW_TEST_RUNNER_SETUP(PARC_ArrayList) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_RUNNER_TEARDOWN(PARC_ArrayList) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestRunner_GetName(testRunner), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, PARC_ArrayList_Add); + LONGBOW_RUN_TEST_CASE(Global, PARC_ArrayList_AddAll); + LONGBOW_RUN_TEST_CASE(Global, PARC_ArrayList_Copy); + LONGBOW_RUN_TEST_CASE(Global, PARC_ArrayList_Destroy); + + LONGBOW_RUN_TEST_CASE(Global, PARC_ArrayList_CustomDestroyer); + + LONGBOW_RUN_TEST_CASE(Global, PARC_ArrayList_Equals_Contract); + LONGBOW_RUN_TEST_CASE(Global, PARC_ArrayList_Equals_Contract_Deep); + + LONGBOW_RUN_TEST_CASE(Global, PARC_ArrayList_FromInitialCapacity); + LONGBOW_RUN_TEST_CASE(Global, PARC_ArrayList_Get); + LONGBOW_RUN_TEST_CASE(Global, PARC_ArrayList_New); + LONGBOW_RUN_TEST_CASE(Global, PARC_ArrayList_Size); + LONGBOW_RUN_TEST_CASE(Global, PARC_ArrayList_Remove_AtIndex_First); + LONGBOW_RUN_TEST_CASE(Global, PARC_ArrayList_Remove_AtIndex); + LONGBOW_RUN_TEST_CASE(Global, PARC_ArrayList_Remove_AtIndex_Last); + LONGBOW_RUN_TEST_CASE(Global, PARC_ArrayList_RemoveAndDestroy_AtIndex_First); + LONGBOW_RUN_TEST_CASE(Global, PARC_ArrayList_RemoveAndDestroy_AtIndex); + LONGBOW_RUN_TEST_CASE(Global, PARC_ArrayList_RemoveAndDestroy_AtIndex_Last); + LONGBOW_RUN_TEST_CASE(Global, PARC_ArrayList_InsertAtIndex); + LONGBOW_RUN_TEST_CASE(Global, PARC_ArrayList_InsertAtIndex_Empty); + LONGBOW_RUN_TEST_CASE(Global, PARC_ArrayList_InsertAtIndex_First); + LONGBOW_RUN_TEST_CASE(Global, PARC_ArrayList_InsertAtIndex_Last); + LONGBOW_RUN_TEST_CASE(Global, PARC_ArrayList_IsEmpty); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks %d memory allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, PARC_ArrayList_Add) +{ + PARCArrayList *array = parcArrayList_Create(parcArrayList_StdlibFreeFunction); + + parcArrayList_Add(array, 0); + size_t actual = parcArrayList_Size(array); + + assertTrue(1 == actual, "Expected=%d, actual=%zu", 1, actual); + + parcArrayList_Destroy(&array); +} + +LONGBOW_TEST_CASE(Global, PARC_ArrayList_AddAll) +{ + PARCArrayList *array = parcArrayList_Create(parcArrayList_StdlibFreeFunction); + + void *elements[] = { + strdup("a"), + strdup("b"), + strdup("c"), + }; + + parcArrayList_AddAll(array, elements, 3); + size_t actual = parcArrayList_Size(array); + + assertTrue(3 == actual, "Expected=%d, actual=%zu", 3, actual); + + parcArrayList_Destroy(&array); +} + +LONGBOW_TEST_CASE(Global, PARC_ArrayList_Copy) +{ + char a[] = "apple"; + char b[] = "bananna"; + char c[] = "cherry"; + + PARCArrayList *array = parcArrayList_Create(NULL); + parcArrayList_Add(array, a); + parcArrayList_Add(array, b); + parcArrayList_Add(array, c); + + PARCArrayList *copy = parcArrayList_Copy(array); + assertTrue(parcArrayList_Equals(array, copy), "Expected arrays to be equal."); + + parcArrayList_Destroy(©); + parcArrayList_Destroy(&array); +} + +static void +testCustomDestroyer(void **bufferVoidPtr) +{ + PARCBuffer **bufferPtr = (PARCBuffer **) bufferVoidPtr; + parcBuffer_Release(bufferPtr); +} + +LONGBOW_TEST_CASE(Global, PARC_ArrayList_CustomDestroyer) +{ + size_t before = parcMemory_Outstanding(); + + PARCArrayList *array = parcArrayList_Create(testCustomDestroyer); + PARCBuffer *buffer = parcBuffer_Allocate(20); + parcArrayList_Add(array, parcBuffer_Acquire(buffer)); + parcBuffer_Release(&buffer); + parcArrayList_Destroy(&array); + + size_t after = parcMemory_Outstanding(); + + assertTrue(before == after, "Memory imbalance after using custom destroy, expected %zu got %zu", before, after); +} + + +LONGBOW_TEST_CASE(Global, PARC_ArrayList_Destroy) +{ + PARCArrayList *array = parcArrayList_Create(parcArrayList_StdlibFreeFunction); + + parcArrayList_Destroy(&array); + assertNull(array, "Expected null."); +} + +LONGBOW_TEST_CASE(Global, PARC_ArrayList_Equals_Empty) +{ + PARCArrayList *a = parcArrayList_Create(parcArrayList_StdlibFreeFunction); + PARCArrayList *b = parcArrayList_Create(parcArrayList_StdlibFreeFunction); + assertTrue(parcArrayList_Equals(a, b), "Equal values were expected to be equal"); + + parcArrayList_Destroy(&a); + parcArrayList_Destroy(&b); +} + +LONGBOW_TEST_CASE(Global, PARC_ArrayList_Equals_Same) +{ + PARCArrayList *a = parcArrayList_Create(parcArrayList_StdlibFreeFunction); + assertTrue(parcArrayList_Equals(a, a), "Expected the same array list to be equal to itself."); + + parcArrayList_Destroy(&a); +} + +LONGBOW_TEST_CASE(Global, PARC_ArrayList_Equals_Contract) +{ + char a[] = "apple"; + char b[] = "bananna"; + char c[] = "cherry"; + char d[] = "potato"; + + PARCArrayList *x = parcArrayList_Create(NULL); + parcArrayList_Add(x, a); + parcArrayList_Add(x, b); + parcArrayList_Add(x, c); + + PARCArrayList *y = parcArrayList_Create(NULL); + parcArrayList_Add(y, a); + parcArrayList_Add(y, b); + parcArrayList_Add(y, c); + + PARCArrayList *z = parcArrayList_Create(NULL); + parcArrayList_Add(z, a); + parcArrayList_Add(z, b); + parcArrayList_Add(z, c); + + PARCArrayList *u1 = parcArrayList_Create(NULL); + parcArrayList_Add(u1, a); + parcArrayList_Add(u1, b); + + PARCArrayList *u2 = parcArrayList_Create(NULL); + parcArrayList_Add(u1, a); + parcArrayList_Add(u2, b); + parcArrayList_Add(u2, c); + parcArrayList_Add(u2, c); + + PARCArrayList *u3 = parcArrayList_Create(NULL); + parcArrayList_Add(u3, a); + parcArrayList_Add(u3, b); + parcArrayList_Add(u3, d); + + parcObjectTesting_AssertEqualsFunction(parcArrayList_Equals, x, y, z, u1, u2, u3); + + parcArrayList_Destroy(&x); + parcArrayList_Destroy(&y); + parcArrayList_Destroy(&z); + parcArrayList_Destroy(&u1); + parcArrayList_Destroy(&u2); + parcArrayList_Destroy(&u3); +} + +static bool +stringEquals(void *x, void *y) +{ + return strcmp((char *) x, (char *) y) == 0; +} + +LONGBOW_TEST_CASE(Global, PARC_ArrayList_Equals_Contract_Deep) +{ + char a[] = "apple"; + char b[] = "bananna"; + char c[] = "cherry"; + char d[] = "potato"; + + PARCArrayList *x = parcArrayList_Create_Capacity(stringEquals, NULL, 0); + parcArrayList_Add(x, a); + parcArrayList_Add(x, b); + parcArrayList_Add(x, c); + + PARCArrayList *y = parcArrayList_Create_Capacity(stringEquals, NULL, 0); + parcArrayList_Add(y, a); + parcArrayList_Add(y, b); + parcArrayList_Add(y, c); + + PARCArrayList *z = parcArrayList_Create_Capacity(stringEquals, NULL, 0); + parcArrayList_Add(z, a); + parcArrayList_Add(z, b); + parcArrayList_Add(z, c); + + PARCArrayList *u1 = parcArrayList_Create_Capacity(stringEquals, NULL, 0); + parcArrayList_Add(u1, a); + parcArrayList_Add(u1, b); + + PARCArrayList *u2 = parcArrayList_Create_Capacity(stringEquals, NULL, 0); + parcArrayList_Add(u2, a); + parcArrayList_Add(u2, b); + parcArrayList_Add(u2, c); + parcArrayList_Add(u2, c); + + PARCArrayList *u3 = parcArrayList_Create_Capacity(stringEquals, NULL, 0); + parcArrayList_Add(u3, a); + parcArrayList_Add(u3, b); + parcArrayList_Add(u3, d); + + parcObjectTesting_AssertEqualsFunction(parcArrayList_Equals, x, y, z, u1, u2, u3); + + parcArrayList_Destroy(&x); + parcArrayList_Destroy(&y); + parcArrayList_Destroy(&z); + parcArrayList_Destroy(&u1); + parcArrayList_Destroy(&u2); + parcArrayList_Destroy(&u3); +} + +LONGBOW_TEST_CASE(Local, PARC_ArrayList_EnsureRemaining_Empty) +{ + PARCArrayList *array = parcArrayList_Create(NULL); + size_t expected = 4; + _ensureRemaining(array, expected); + + size_t actual = _remaining(array); + + assertTrue(actual >= expected, "Expected >= %zd, actual=%zd", expected, actual); + parcArrayList_Destroy(&array); +} + +LONGBOW_TEST_CASE(Local, PARC_ArrayList_EnsureRemaining_NonEmpty) +{ + PARCArrayList *array = parcArrayList_Create(NULL); + parcArrayList_Add(array, 0); + + size_t expected = 5; + _ensureRemaining(array, expected); + + size_t actual = _remaining(array); + + assertTrue(actual >= expected, "Expected >= %zd, actual=%zd", expected, actual); + parcArrayList_Destroy(&array); +} + +LONGBOW_TEST_CASE(Global, PARC_ArrayList_FromInitialCapacity) +{ + PARCArrayList *array = parcArrayList_Create_Capacity(NULL, parcArrayList_StdlibFreeFunction, 10); + size_t actual = parcArrayList_Size(array); + + assertTrue(0 == actual, "Expected=%d, actual=%zu", 0, actual); + + parcArrayList_Destroy(&array); +} + +LONGBOW_TEST_CASE(Global, PARC_ArrayList_Get) +{ + PARCArrayList *array = parcArrayList_Create(parcArrayList_StdlibFreeFunction); + + char *expected = strdup("Hello World"); + parcArrayList_Add(array, expected); + + char *actual = parcArrayList_Get(array, 0); + + assertTrue(expected == actual, "Expected=%p, actual=%p", (void *) expected, (void *) actual); + + parcArrayList_Destroy(&array); +} + +LONGBOW_TEST_CASE(Global, PARC_ArrayList_New) +{ + PARCArrayList *array = parcArrayList_Create(parcArrayList_StdlibFreeFunction); + size_t size = parcArrayList_Size(array); + assertTrue(0 == size, "Expected %d actual=%zu", 0, size); + + parcArrayList_Destroy(&array); +} + +LONGBOW_TEST_CASE(Global, PARC_ArrayList_Size) +{ + PARCArrayList *array = parcArrayList_Create(NULL); + parcArrayList_Add(array, 0); + + size_t size = parcArrayList_Size(array); + assertTrue(1 == size, "Expected %d actual=%zu", 1, size); + parcArrayList_Destroy(&array); +} + +LONGBOW_TEST_CASE(Global, PARC_ArrayList_IsEmpty) +{ + PARCArrayList *array = parcArrayList_Create(NULL); + assertTrue(parcArrayList_IsEmpty(array), "Expected a new array to be empty."); + + parcArrayList_Add(array, 0); + assertFalse(parcArrayList_IsEmpty(array), "Expected an array with more than zero elements to be empty."); + + parcArrayList_Destroy(&array); +} + +LONGBOW_TEST_CASE(Global, PARC_ArrayList_InsertAtIndex) +{ + PARCArrayList *array = parcArrayList_Create(NULL); + + parcArrayList_Add(array, (void *) 1); + parcArrayList_Add(array, (void *) 2); + size_t actual = parcArrayList_Size(array); + + assertTrue(2 == actual, "Expected=%d, actual=%zu", 2, actual); + + parcArrayList_InsertAtIndex(array, 1, (void *) 3); + + actual = parcArrayList_Size(array); + assertTrue(3 == actual, "Expected=%d, actual=%zu", 3, actual); + + void *element0 = parcArrayList_Get(array, 0); + assertTrue(element0 == (void *) 1, "Element 1 moved?"); + + void *element1 = parcArrayList_Get(array, 1); + assertTrue(element1 == (void *) 3, "Element 1 moved?"); + + void *element2 = parcArrayList_Get(array, 2); + assertTrue(element2 == (void *) 2, "Element 1 moved?"); + + parcArrayList_Destroy(&array); +} + +LONGBOW_TEST_CASE(Global, PARC_ArrayList_InsertAtIndex_Empty) +{ + PARCArrayList *array = parcArrayList_Create(NULL); + + parcArrayList_InsertAtIndex(array, 0, (void *) 3); + + size_t actual = parcArrayList_Size(array); + + assertTrue(1 == actual, "Expected=%d, actual=%zu", 1, actual); + + parcArrayList_Destroy(&array); +} + +LONGBOW_TEST_CASE(Global, PARC_ArrayList_InsertAtIndex_First) +{ + PARCArrayList *array = parcArrayList_Create(NULL); + + parcArrayList_Add(array, (void *) 1); + parcArrayList_InsertAtIndex(array, 0, (void *) 2); + size_t actual = parcArrayList_Size(array); + + assertTrue(2 == actual, "Expected=%d, actual=%zu", 2, actual); + + void *element0 = parcArrayList_Get(array, 0); + assertTrue(element0 == (void *) 2, "Element 1 moved?"); + + void *element1 = parcArrayList_Get(array, 1); + assertTrue(element1 == (void *) 1, "Element 1 moved?"); + + parcArrayList_Destroy(&array); +} + +LONGBOW_TEST_CASE(Global, PARC_ArrayList_InsertAtIndex_Last) +{ + PARCArrayList *array = parcArrayList_Create(NULL); + + parcArrayList_Add(array, (void *) 1); + parcArrayList_Add(array, (void *) 2); + size_t actual = parcArrayList_Size(array); + + assertTrue(2 == actual, "Expected=%d, actual=%zu", 2, actual); + + parcArrayList_InsertAtIndex(array, 2, (void *) 3); + + actual = parcArrayList_Size(array); + assertTrue(3 == actual, "Expected=%d, actual=%zu", 3, actual); + + void *element0 = parcArrayList_Get(array, 0); + assertTrue(element0 == (void *) 1, "Element 1 moved?"); + + void *element1 = parcArrayList_Get(array, 1); + assertTrue(element1 == (void *) 2, "Element 1 moved?"); + + void *element2 = parcArrayList_Get(array, 2); + assertTrue(element2 == (void *) 3, "Element 1 moved?"); + + parcArrayList_Destroy(&array); +} + +LONGBOW_TEST_CASE(Global, PARC_ArrayList_Remove_AtIndex_First) +{ + char a[] = "apple"; + char b[] = "bananna"; + char c[] = "cherry"; + + PARCArrayList *array = parcArrayList_Create(NULL); + parcArrayList_Add(array, a); + parcArrayList_Add(array, b); + parcArrayList_Add(array, c); + + PARCArrayList *expected = parcArrayList_Create(NULL); + parcArrayList_Add(expected, b); + parcArrayList_Add(expected, c); + + void *removedElement = parcArrayList_RemoveAtIndex(array, 0); + + assertTrue(removedElement == a, "Expected "); + assertTrue(parcArrayList_Equals(expected, array), "Expected "); + + parcArrayList_Destroy(&expected); + parcArrayList_Destroy(&array); +} + +LONGBOW_TEST_CASE(Global, PARC_ArrayList_Remove_AtIndex) +{ + char a[] = "apple"; + char b[] = "bananna"; + char c[] = "cherry"; + + PARCArrayList *array = parcArrayList_Create(NULL); + parcArrayList_Add(array, a); + parcArrayList_Add(array, b); + parcArrayList_Add(array, c); + + PARCArrayList *expected = parcArrayList_Create(NULL); + parcArrayList_Add(expected, a); + parcArrayList_Add(expected, c); + + void *removedElement = parcArrayList_RemoveAtIndex(array, 1); + + assertTrue(removedElement == b, "Expected "); + assertTrue(parcArrayList_Equals(expected, array), "Expected "); + + parcArrayList_Destroy(&expected); + parcArrayList_Destroy(&array); +} + + +LONGBOW_TEST_CASE(Global, PARC_ArrayList_Remove_AtIndex_Last) +{ + char a[] = "apple"; + char b[] = "bananna"; + char c[] = "cherry"; + + PARCArrayList *array = parcArrayList_Create(NULL); + parcArrayList_Add(array, a); + parcArrayList_Add(array, b); + parcArrayList_Add(array, c); + + PARCArrayList *expected = parcArrayList_Create(NULL); + parcArrayList_Add(expected, a); + parcArrayList_Add(expected, b); + + void *removedElement = parcArrayList_RemoveAtIndex(array, 2); + + assertTrue(removedElement == c, "Expected "); + assertTrue(parcArrayList_Equals(expected, array), "Expected "); + + parcArrayList_Destroy(&expected); + parcArrayList_Destroy(&array); +} + +LONGBOW_TEST_CASE(Global, PARC_ArrayList_RemoveAndDestroy_AtIndex_First) +{ + char a[] = "apple"; + char b[] = "bananna"; + char c[] = "cherry"; + + PARCArrayList *array = parcArrayList_Create(NULL); + parcArrayList_Add(array, a); + parcArrayList_Add(array, b); + parcArrayList_Add(array, c); + + PARCArrayList *expected = parcArrayList_Create(NULL); + parcArrayList_Add(expected, b); + parcArrayList_Add(expected, c); + + parcArrayList_RemoveAndDestroyAtIndex(array, 0); + + assertTrue(parcArrayList_Equals(expected, array), "Expected "); + + parcArrayList_Destroy(&expected); + parcArrayList_Destroy(&array); +} + +LONGBOW_TEST_CASE(Global, PARC_ArrayList_RemoveAndDestroy_AtIndex) +{ + char a[] = "apple"; + char b[] = "bananna"; + char c[] = "cherry"; + + PARCArrayList *array = parcArrayList_Create(NULL); + parcArrayList_Add(array, a); + parcArrayList_Add(array, b); + parcArrayList_Add(array, c); + + PARCArrayList *expected = parcArrayList_Create(NULL); + parcArrayList_Add(expected, a); + parcArrayList_Add(expected, c); + + parcArrayList_RemoveAndDestroyAtIndex(array, 1); + + assertTrue(parcArrayList_Equals(expected, array), "Expected "); + + parcArrayList_Destroy(&expected); + parcArrayList_Destroy(&array); +} + +LONGBOW_TEST_CASE(Global, PARC_ArrayList_RemoveAndDestroy_AtIndex_Last) +{ + char a[] = "apple"; + char b[] = "bananna"; + char c[] = "cherry"; + + PARCArrayList *array = parcArrayList_Create(NULL); + parcArrayList_Add(array, a); + parcArrayList_Add(array, b); + parcArrayList_Add(array, c); + + PARCArrayList *expected = parcArrayList_Create(NULL); + parcArrayList_Add(expected, a); + parcArrayList_Add(expected, b); + + parcArrayList_RemoveAndDestroyAtIndex(array, 2); + + assertTrue(parcArrayList_Equals(expected, array), "Expected "); + + parcArrayList_Destroy(&expected); + parcArrayList_Destroy(&array); +} + +LONGBOW_TEST_FIXTURE(Local) +{ + LONGBOW_RUN_TEST_CASE(Local, PARC_ArrayList_EnsureRemaining_Empty); + LONGBOW_RUN_TEST_CASE(Local, PARC_ArrayList_EnsureRemaining_NonEmpty); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Errors) +{ + LONGBOW_RUN_TEST_CASE(Errors, PARC_ArrayList_InsertAtIndex_OutOfCapacity); +} + +LONGBOW_TEST_FIXTURE_SETUP(Errors) +{ + PARCArrayList *array = parcArrayList_Create(NULL); + + longBowTestCase_SetClipBoardData(testCase, array); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Errors) +{ + PARCArrayList *array = longBowTestCase_GetClipBoardData(testCase); + parcArrayList_Destroy(&array); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("Errors %s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE_EXPECTS(Errors, PARC_ArrayList_InsertAtIndex_OutOfCapacity, .event = &LongBowAssertEvent) +{ + PARCArrayList *array = longBowTestCase_GetClipBoardData(testCase); + + parcArrayList_Add(array, (void *) 1); + parcArrayList_Add(array, (void *) 2); + + parcArrayList_InsertAtIndex(array, 200, (void *) 3); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(PARC_ArrayList); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_AtomicInteger.c b/libparc/parc/algol/test/test_parc_AtomicInteger.c new file mode 100644 index 00000000..9bd1b99a --- /dev/null +++ b/libparc/parc/algol/test/test_parc_AtomicInteger.c @@ -0,0 +1,156 @@ +/* + * 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 the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../parc_AtomicInteger.c" + +#include <inttypes.h> +#include <stdio.h> + +#include <LongBow/testing.h> +#include <LongBow/debugging.h> + +LONGBOW_TEST_RUNNER(test_parc_AtomicInteger) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Threaded); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(test_parc_AtomicInteger) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(test_parc_AtomicInteger) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcAtomicInteger_Uint32Increment); + LONGBOW_RUN_TEST_CASE(Global, parcAtomicInteger_Uint32Decrement); + LONGBOW_RUN_TEST_CASE(Global, parcAtomicInteger_Uint64Increment); + LONGBOW_RUN_TEST_CASE(Global, parcAtomicInteger_Uint64Decrement); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcAtomicInteger_Uint32Increment) +{ + uint32_t value = 0; + parcAtomicInteger_Uint32Increment(&value); + assertTrue(value == 1, "Expected 1, actual, %u", value); +} + +LONGBOW_TEST_CASE(Global, parcAtomicInteger_Uint32Decrement) +{ + uint32_t value = 0; + parcAtomicInteger_Uint32Increment(&value); + assertTrue(value == 1, "Expected 1, actual, %u", value); +} + +LONGBOW_TEST_CASE(Global, parcAtomicInteger_Uint64Increment) +{ + uint64_t value = 0; + parcAtomicInteger_Uint64Increment(&value); + assertTrue(value == 1, "Expected 1, actual, %" PRIu64 "", value); +} + +LONGBOW_TEST_CASE(Global, parcAtomicInteger_Uint64Decrement) +{ + uint64_t value = 0; + parcAtomicInteger_Uint64Increment(&value); + assertTrue(value == 1, "Expected 1, actual, %" PRIu64 "", value); +} + +LONGBOW_TEST_FIXTURE(Threaded) +{ + LONGBOW_RUN_TEST_CASE(Threaded, collaborative); +} + +LONGBOW_TEST_FIXTURE_SETUP(Threaded) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Threaded) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +static void * +collaborator_A(void *data) +{ + uint32_t *valuePointer = (uint32_t *) data; + uint32_t contribution = 0; + while (*valuePointer < 1000000) { + parcAtomicInteger_Uint32Increment(valuePointer); + contribution++; + } + printf("A contribution %d\n", contribution); + pthread_exit((void *) NULL); +} + +static void * +collaborator_B(void *data) +{ + uint32_t *valuePointer = (uint32_t *) data; + + uint32_t contribution = 0; + while (*valuePointer < 1000000) { + parcAtomicInteger_Uint32Increment(valuePointer); + contribution++; + } + + printf("B contribution %d\n", contribution); + pthread_exit((void *) NULL); +} + +LONGBOW_TEST_CASE(Threaded, collaborative) +{ + uint32_t value = 0; + + pthread_t thread_A; + pthread_t thread_B; + + pthread_create(&thread_A, NULL, collaborator_A, &value); + pthread_create(&thread_B, NULL, collaborator_B, &value); + + pthread_join(thread_A, NULL); + pthread_join(thread_B, NULL); +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(test_parc_AtomicInteger); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_Base64.c b/libparc/parc/algol/test/test_parc_Base64.c new file mode 100755 index 00000000..4509ded3 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_Base64.c @@ -0,0 +1,414 @@ +/* + * 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 <LongBow/unit-test.h> + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../parc_Base64.c" +#include <parc/algol/parc_SafeMemory.h> + +LONGBOW_TEST_RUNNER(parc_Base64) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_Base64) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_Base64) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcBase64_Decode); + LONGBOW_RUN_TEST_CASE(Global, parcBase64_Decode_Linefeeds); + LONGBOW_RUN_TEST_CASE(Global, parcBase64_Encode); + LONGBOW_RUN_TEST_CASE(Global, parcBase64_Encode_Binary); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks %d memory allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +static struct testvector_s { + char *plaintext; + char *encoded; +} testvector[] = { + // Test vectors from RFC 4648 + { .plaintext = "", .encoded = "" }, + { .plaintext = "f", .encoded = "Zg==" }, + { .plaintext = "fo", .encoded = "Zm8=" }, + { .plaintext = "foo", .encoded = "Zm9v" }, + { .plaintext = "foob", .encoded = "Zm9vYg==" }, + { .plaintext = "fooba", .encoded = "Zm9vYmE=" }, + { .plaintext = "foobar", .encoded = "Zm9vYmFy" }, + { .plaintext = NULL, .encoded = NULL } +}; + +LONGBOW_TEST_CASE(Global, parcBase64_Decode) +{ + int i = 0; + while (testvector[i].plaintext != NULL) { + PARCBufferComposer *input = parcBufferComposer_Create(); + PARCBufferComposer *truth = parcBufferComposer_Create(); + PARCBufferComposer *output = parcBufferComposer_Create(); + + parcBufferComposer_PutString(input, testvector[i].encoded); + PARCBuffer *inputBuffer = parcBufferComposer_ProduceBuffer(input); + parcBufferComposer_PutString(truth, testvector[i].plaintext); + PARCBuffer *truthBuffer = parcBufferComposer_ProduceBuffer(truth); + + parcBase64_Decode(output, parcBufferComposer_GetBuffer(input)); + PARCBuffer *outputBuffer = parcBufferComposer_ProduceBuffer(output); + + assertTrue(parcBuffer_Equals(truthBuffer, outputBuffer), + "encoding, expected '%s' got '%s'", + parcBuffer_ToHexString(truthBuffer), + parcBuffer_ToHexString(outputBuffer)); + + i++; + + parcBuffer_Release(&inputBuffer); + parcBuffer_Release(&truthBuffer); + parcBuffer_Release(&outputBuffer); + + parcBufferComposer_Release(&input); + parcBufferComposer_Release(&output); + parcBufferComposer_Release(&truth); + } +} + +LONGBOW_TEST_CASE(Global, parcBase64_Decode_Linefeeds) +{ + PARCBufferComposer *input = parcBufferComposer_Create(); + PARCBufferComposer *truth = parcBufferComposer_Create(); + PARCBufferComposer *output = parcBufferComposer_Create(); + + char plaintext[] = "It was a dark and stormy night, and all through the code not bit was stirring.\x0A"; + char encoded_with_crlf[] = "SXQg" "\x0D\x0A" "d2FzIGEgZGFyayBhbmQgc3Rvcm15IG5pZ2h0LCBhbmQgYWxsIHRocm91Z2gg" "\x0D\x0A" "dGhlIGNvZGUgbm90IGJpdCB3YXMgc3RpcnJpbmcuCg=="; + + parcBufferComposer_PutString(input, encoded_with_crlf); + PARCBuffer *inputBuffer = parcBufferComposer_ProduceBuffer(input); + + parcBufferComposer_PutString(truth, plaintext); + PARCBuffer *truthBuffer = parcBufferComposer_ProduceBuffer(truth); + + parcBase64_Decode(output, parcBufferComposer_GetBuffer(input)); + PARCBuffer *outputBuffer = parcBufferComposer_ProduceBuffer(output); + assertTrue(parcBuffer_Equals(truthBuffer, outputBuffer), + "encoding, expected '%s' got '%s'", + parcBuffer_ToHexString(truthBuffer), + parcBuffer_ToHexString(outputBuffer)); + + parcBuffer_Release(&inputBuffer); + parcBuffer_Release(&truthBuffer); + parcBuffer_Release(&outputBuffer); + + parcBufferComposer_Release(&input); + parcBufferComposer_Release(&output); + parcBufferComposer_Release(&truth); +} + + +LONGBOW_TEST_CASE(Global, parcBase64_Encode) +{ + int i = 0; + while (testvector[i].plaintext != NULL) { + PARCBufferComposer *input = parcBufferComposer_Create(); + PARCBufferComposer *truth = parcBufferComposer_Create(); + PARCBufferComposer *output = parcBufferComposer_Create(); + + parcBufferComposer_PutString(input, testvector[i].plaintext); + PARCBuffer *inputBuffer = parcBufferComposer_ProduceBuffer(input); + parcBufferComposer_PutString(truth, testvector[i].encoded); + PARCBuffer *truthBuffer = parcBufferComposer_ProduceBuffer(truth); + + parcBase64_Encode(output, parcBufferComposer_GetBuffer(input)); + PARCBuffer *outputBuffer = parcBufferComposer_ProduceBuffer(output); + assertTrue(parcBuffer_Equals(truthBuffer, outputBuffer), + "encoding, expected '%s' got '%s'", + parcBuffer_ToHexString(truthBuffer), + parcBuffer_ToHexString(outputBuffer)); + + i++; + + parcBuffer_Release(&inputBuffer); + parcBuffer_Release(&truthBuffer); + parcBuffer_Release(&outputBuffer); + + parcBufferComposer_Release(&input); + parcBufferComposer_Release(&output); + parcBufferComposer_Release(&truth); + } +} + +LONGBOW_TEST_CASE(Global, parcBase64_Encode_Binary) +{ + uint8_t zero[] = { 0 }; + + // 1 2 3 4 5 6 7 + char *truthvector[] = { "AA==", "AAA=", "AAAA", "AAAAAA==", "AAAAAAA=", "AAAAAAAA", "AAAAAAAAAA==" }; + + for (int i = 0; i < sizeof(truthvector) / sizeof(truthvector[0]); i++) { + PARCBufferComposer *input = parcBufferComposer_Create(); + PARCBufferComposer *truth = parcBufferComposer_Create(); + PARCBufferComposer *output = parcBufferComposer_Create(); + + parcBufferComposer_PutArray(truth, (uint8_t *) truthvector[i], strlen(truthvector[i])); + PARCBuffer *truthBuffer = parcBufferComposer_ProduceBuffer(truth); + + // just keep apending a zero to make it longer + parcBuffer_SetLimit(parcBufferComposer_GetBuffer(input), parcBuffer_Capacity(parcBufferComposer_GetBuffer(input))); + parcBuffer_SetPosition(parcBufferComposer_GetBuffer(input), i); + parcBufferComposer_PutArray(input, zero, 1); + PARCBuffer *inputBuffer = parcBufferComposer_ProduceBuffer(input); + + parcBase64_Encode(output, parcBufferComposer_GetBuffer(input)); + PARCBuffer *outputBuffer = parcBufferComposer_ProduceBuffer(output); + + assertTrue(parcBuffer_Equals(truthBuffer, outputBuffer), + "encoding, expected '%s' got '%s'", + parcBuffer_ToHexString(truthBuffer), + parcBuffer_ToHexString(outputBuffer)); + + parcBuffer_Release(&inputBuffer); + parcBuffer_Release(&truthBuffer); + parcBuffer_Release(&outputBuffer); + + parcBufferComposer_Release(&input); + parcBufferComposer_Release(&output); + parcBufferComposer_Release(&truth); + } +} + +LONGBOW_TEST_FIXTURE(Local) +{ + LONGBOW_RUN_TEST_CASE(Local, encodeWithPad_0); + LONGBOW_RUN_TEST_CASE(Local, encodeWithPad_1); + LONGBOW_RUN_TEST_CASE(Local, encodeWithPad_2); + LONGBOW_RUN_TEST_CASE(Local, decode_invalid); + LONGBOW_RUN_TEST_CASE(Local, decode_1); + LONGBOW_RUN_TEST_CASE(Local, decode_2); + LONGBOW_RUN_TEST_CASE(Local, decode_3); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks %d memory allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +/** + * This will encode "foo" + */ +LONGBOW_TEST_CASE(Local, encodeWithPad_0) +{ + PARCBufferComposer *output = parcBufferComposer_Create(); + uint8_t input[] = "foobar"; + PARCBufferComposer *truth = parcBufferComposer_Create(); + parcBufferComposer_PutString(truth, "Zm9v"); + PARCBuffer *truthBuffer = parcBufferComposer_ProduceBuffer(truth); + + _encodeWithPad(output, input, 0); + PARCBuffer *outputBuffer = parcBufferComposer_ProduceBuffer(output); + assertTrue(parcBuffer_Equals(truthBuffer, outputBuffer), + "Failed 3-byte encode, expected '%s' got '%s'", + parcBuffer_ToHexString(truthBuffer), + parcBuffer_ToHexString(outputBuffer)); + + parcBuffer_Release(&truthBuffer); + parcBuffer_Release(&outputBuffer); + + parcBufferComposer_Release(&output); + parcBufferComposer_Release(&truth); +} + +/** + * This will encode "fo" because we tell it there's 1 pad byte + */ +LONGBOW_TEST_CASE(Local, encodeWithPad_1) +{ + PARCBufferComposer *output = parcBufferComposer_Create(); + uint8_t input[] = "foobar"; + PARCBufferComposer *truth = parcBufferComposer_Create(); + parcBufferComposer_PutString(truth, "Zm8="); + PARCBuffer *truthBuffer = parcBufferComposer_ProduceBuffer(truth); + + _encodeWithPad(output, input, 1); + PARCBuffer *outputBuffer = parcBufferComposer_ProduceBuffer(output); + + assertTrue(parcBuffer_Equals(truthBuffer, outputBuffer), + "Failed 3-byte encode, expected '%s' got '%s'", + parcBuffer_ToHexString(truthBuffer), + parcBuffer_ToHexString(outputBuffer)); + + parcBuffer_Release(&truthBuffer); + parcBuffer_Release(&outputBuffer); + + parcBufferComposer_Release(&output); + parcBufferComposer_Release(&truth); +} + +/** + * This will encode "f" because we tell it there's 2 pad byte + */ +LONGBOW_TEST_CASE(Local, encodeWithPad_2) +{ + PARCBufferComposer *output = parcBufferComposer_Create(); + uint8_t input[] = "foobar"; + PARCBufferComposer *truth = parcBufferComposer_Create(); + parcBufferComposer_PutString(truth, "Zg=="); + PARCBuffer *truthBuffer = parcBufferComposer_ProduceBuffer(truth); + + _encodeWithPad(output, input, 2); + PARCBuffer *outputBuffer = parcBufferComposer_ProduceBuffer(output); + + assertTrue(parcBuffer_Equals(truthBuffer, outputBuffer), + "Failed 3-byte encode, expected '%s' got '%s'", + parcBuffer_ToHexString(truthBuffer), + parcBuffer_ToHexString(outputBuffer)); + + parcBuffer_Release(&truthBuffer); + parcBuffer_Release(&outputBuffer); + + parcBufferComposer_Release(&output); + parcBufferComposer_Release(&truth); +} + + +LONGBOW_TEST_CASE(Local, decode_1) +{ + PARCBufferComposer *output = parcBufferComposer_Create(); + uint8_t input[] = "Zg=="; + PARCBufferComposer *truth = parcBufferComposer_Create(); + parcBufferComposer_PutString(truth, "f"); + PARCBuffer *truthBuffer = parcBufferComposer_ProduceBuffer(truth); + + bool success = _decode(output, input); + assertTrue(success, "Valid base64 failed decode"); + + PARCBuffer *outputBuffer = parcBufferComposer_ProduceBuffer(output); + assertTrue(parcBuffer_Equals(truthBuffer, outputBuffer), + "Failed 3-byte encode, expected '%s' got '%s'", + parcBuffer_ToHexString(truthBuffer), + parcBuffer_ToHexString(outputBuffer)); + + parcBuffer_Release(&truthBuffer); + parcBuffer_Release(&outputBuffer); + + parcBufferComposer_Release(&output); + parcBufferComposer_Release(&truth); +} + +LONGBOW_TEST_CASE(Local, decode_2) +{ + PARCBufferComposer *output = parcBufferComposer_Create(); + uint8_t input[] = "Zm8="; + PARCBufferComposer *truth = parcBufferComposer_Create(); + parcBufferComposer_PutString(truth, "fo"); + PARCBuffer *truthBuffer = parcBufferComposer_ProduceBuffer(truth); + + bool success = _decode(output, input); + assertTrue(success, "Valid base64 failed decode"); + + PARCBuffer *outputBuffer = parcBufferComposer_ProduceBuffer(output); + + assertTrue(parcBuffer_Equals(truthBuffer, outputBuffer), + "Failed 3-byte encode, expected '%s' got '%s'", + parcBuffer_ToHexString(truthBuffer), + parcBuffer_ToHexString(outputBuffer)); + + parcBuffer_Release(&truthBuffer); + parcBuffer_Release(&outputBuffer); + + parcBufferComposer_Release(&output); + parcBufferComposer_Release(&truth); +} + +LONGBOW_TEST_CASE(Local, decode_3) +{ + PARCBufferComposer *output = parcBufferComposer_Create(); + uint8_t input[] = "Zm9v"; + PARCBufferComposer *truth = parcBufferComposer_Create(); + parcBufferComposer_PutString(truth, "foo"); + PARCBuffer *truthBuffer = parcBufferComposer_ProduceBuffer(truth); + + bool success = _decode(output, input); + assertTrue(success, "Valid base64 failed decode"); + PARCBuffer *outputBuffer = parcBufferComposer_ProduceBuffer(output); + + assertTrue(parcBuffer_Equals(truthBuffer, outputBuffer), + "Failed 3-byte encode, expected '%s' got '%s'", + parcBuffer_ToHexString(truthBuffer), + parcBuffer_ToHexString(outputBuffer)); + + parcBuffer_Release(&truthBuffer); + parcBuffer_Release(&outputBuffer); + + parcBufferComposer_Release(&output); + parcBufferComposer_Release(&truth); +} + +LONGBOW_TEST_CASE(Local, decode_invalid) +{ + PARCBufferComposer *output = parcBufferComposer_Create(); + uint8_t input[] = "@@@@"; + + bool success = _decode(output, input); + assertFalse(success, "Invalid base64 somehow decoded"); + + parcBufferComposer_Release(&output); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_Base64); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_BitVector.c b/libparc/parc/algol/test/test_parc_BitVector.c new file mode 100755 index 00000000..9a4d0806 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_BitVector.c @@ -0,0 +1,438 @@ +/* + * 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 <LongBow/unit-test.h> + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../parc_BitVector.c" + +#include <stdio.h> +#include <parc/algol/parc_SafeMemory.h> +#include <limits.h> + +LONGBOW_TEST_RUNNER(parc_BitVector) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_BitVector) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_BitVector) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcBitVector_Create_Release); + LONGBOW_RUN_TEST_CASE(Global, parcBitVector_SetClear); + LONGBOW_RUN_TEST_CASE(Global, parcBitVector_SetVector); + LONGBOW_RUN_TEST_CASE(Global, parcBitVector_Reset); + LONGBOW_RUN_TEST_CASE(Global, parcBitVector_ClearVector); + LONGBOW_RUN_TEST_CASE(Global, parcBitVector_NextBitSet); + LONGBOW_RUN_TEST_CASE(Global, parcBitVector_Get); + LONGBOW_RUN_TEST_CASE(Global, parcBitVector_ToString); + LONGBOW_RUN_TEST_CASE(Global, parcBitVector_Copy); + LONGBOW_RUN_TEST_CASE(Global, parcBitVector_Equals); + LONGBOW_RUN_TEST_CASE(Global, parcBitVector_Contains); + LONGBOW_RUN_TEST_CASE(Global, parcBitVector_Set); + LONGBOW_RUN_TEST_CASE(Global, parcBitVector_And); + LONGBOW_RUN_TEST_CASE(Global, parcBitVector_Or); + LONGBOW_RUN_TEST_CASE(Global, parcBitVector_Shift); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks %d memory allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcBitVector_Create_Release) +{ + PARCBitVector *parcBitVector = parcBitVector_Create(); + assertTrue(parcBitVector, "parcBitVector_Create returned a NULL pointer"); + PARCBitVector *reference = parcBitVector; + parcBitVector_Acquire(reference); + parcBitVector_Release(&parcBitVector); + parcBitVector_Release(&reference); +} + +LONGBOW_TEST_CASE(Global, parcBitVector_Set) +{ + PARCBitVector *parcBitVector = parcBitVector_Create(); + assertTrue(parcBitVector, "parcBitVector_Create returned a NULL pointer"); + assertTrue(parcBitVector_NumberOfBitsSet(parcBitVector) == 0, "parcBitVector_Create created a non-empty vector"); + + parcBitVector_Set(parcBitVector, 0); + assertTrue(parcBitVector_NumberOfBitsSet(parcBitVector) == 1, "Expect number of bits set to be 1"); + assertTrue(parcBitVector->firstBitSet == 0, "Expect first bit set to be 0"); + assertTrue(parcBitVector->bitLength == 8, "Expect the bitLength to be 8"); + assertTrue(parcBitVector->bitArray[0] == (uint8_t) 1, "Expect the bitArray as a unsigned char to be = 1"); + + parcBitVector_Set(parcBitVector, 7); + assertTrue(parcBitVector_NumberOfBitsSet(parcBitVector) == 2, "Expect number of bits set to be 2"); + assertTrue(parcBitVector->firstBitSet == 0, "Expect first bit set to be 0"); + assertTrue(parcBitVector->bitLength == 8, "Expect the bitLength to be 8"); + assertTrue(parcBitVector->bitArray[0] == (uint8_t) 0x81, "Expect the bitArray as a unsigned char to be = 0x81"); + + parcBitVector_Set(parcBitVector, 8); + assertTrue(parcBitVector_NumberOfBitsSet(parcBitVector) == 3, "Expect number of bits set to be 3"); + assertTrue(parcBitVector->firstBitSet == 0, "Expect first bit set to be 0"); + assertTrue(parcBitVector->bitLength == 16, "Expect the bitLength to be 16"); + assertTrue(parcBitVector->bitArray[0] == (uint8_t) 0x81, "Expect the bitArray as a unsigned char to be = 0x81"); + assertTrue(parcBitVector->bitArray[1] == (uint8_t) 0x1, "Expect the bitArray as a unsigned char to be = 0x1"); + + parcBitVector_Release(&parcBitVector); +} + +LONGBOW_TEST_CASE(Global, parcBitVector_And) +{ + PARCBitVector *vector1 = parcBitVector_Create(); + PARCBitVector *vector2 = parcBitVector_Create(); + + parcBitVector_Set(vector1, 1); + parcBitVector_Set(vector1, 2); + parcBitVector_Set(vector1, 10); + parcBitVector_Set(vector2, 2); + parcBitVector_Set(vector2, 1); + parcBitVector_Set(vector2, 20); + + PARCBitVector *result = parcBitVector_And(vector1, vector2); + + assertTrue(parcBitVector_NumberOfBitsSet(result) == 2, "AND vector not equal to expected results"); + parcBitVector_Release(&result); + + result = parcBitVector_And(vector1, NULL); + assertTrue(parcBitVector_NumberOfBitsSet(result) == 0, "AND vector not equal to expected results"); + parcBitVector_Release(&result); + + result = parcBitVector_And(NULL, vector2); + assertTrue(parcBitVector_NumberOfBitsSet(result) == 0, "AND vector not equal to expected results"); + parcBitVector_Release(&result); + + result = parcBitVector_And(NULL, NULL); + assertTrue(parcBitVector_NumberOfBitsSet(result) == 0, "AND vector not equal to expected results"); + parcBitVector_Release(&result); + + parcBitVector_Release(&vector1); + parcBitVector_Release(&vector2); +} + +LONGBOW_TEST_CASE(Global, parcBitVector_Or) +{ + PARCBitVector *vector1 = parcBitVector_Create(); + PARCBitVector *vector2 = parcBitVector_Create(); + + parcBitVector_Set(vector1, 1); + parcBitVector_Set(vector1, 2); + parcBitVector_Set(vector1, 10); + parcBitVector_Set(vector2, 2); + parcBitVector_Set(vector2, 1); + parcBitVector_Set(vector2, 20); + + PARCBitVector *result = parcBitVector_Or(vector1, vector2); + + assertTrue(parcBitVector_Contains(result, vector1), "Vector contents not included in OR operation results"); + assertTrue(parcBitVector_Contains(result, vector2), "Vector contents not included in OR operation results"); + assertTrue(parcBitVector_NumberOfBitsSet(result) == 4, "OR vector not equal to expected results"); + parcBitVector_Release(&result); + + result = parcBitVector_Or(vector1, NULL); + assertTrue(parcBitVector_Equals(result, vector1), "OR vector not equal to expected results"); + parcBitVector_Release(&result); + + result = parcBitVector_Or(NULL, vector2); + assertTrue(parcBitVector_Equals(result, vector2), "OR vector not equal to expected results"); + parcBitVector_Release(&result); + + result = parcBitVector_Or(NULL, NULL); + assertTrue(parcBitVector_NumberOfBitsSet(result) == 0, "OR vector not equal to expected results"); + parcBitVector_Release(&result); + + parcBitVector_Release(&vector1); + parcBitVector_Release(&vector2); +} + +LONGBOW_TEST_CASE(Global, parcBitVector_Shift) +{ + PARCBitVector *vector = parcBitVector_Create(); + + parcBitVector_Set(vector, 0); // should drop off on left shift + parcBitVector_Set(vector, 11); + parcBitVector_Set(vector, 12); + parcBitVector_Set(vector, 13); + parcBitVector_Set(vector, 22); + parcBitVector_LeftShift(vector, 10); + parcBitVector_RightShift(vector, 10); + assertTrue(parcBitVector_NextBitSet(vector, 0) == 11, "Shift operations failed"); + assertTrue(parcBitVector_NextBitSet(vector, 12) == 12, "Shift operations failed"); + assertTrue(parcBitVector_NextBitSet(vector, 14) == 22, "Shift operations failed"); + assertTrue(parcBitVector_NumberOfBitsSet(vector) == 4, "Shift operations failed to drop first bit on left shift"); + parcBitVector_Release(&vector); +} + +LONGBOW_TEST_CASE(Global, parcBitVector_SetClear) +{ + PARCBitVector *parcBitVector = parcBitVector_Create(); + assertTrue(parcBitVector, "parcBitVector_Create returned a NULL pointer"); + assertTrue(parcBitVector_NumberOfBitsSet(parcBitVector) == 0, "parcBitVector_Create created a non-empty vector"); + + parcBitVector_Set(parcBitVector, 10); + assertTrue(parcBitVector_NumberOfBitsSet(parcBitVector) == 1, "parcBitVector_Set failed"); + + parcBitVector_Clear(parcBitVector, 10); + assertTrue(parcBitVector_NumberOfBitsSet(parcBitVector) == 0, "parcBitVector_Clear failed"); + + parcBitVector_Clear(parcBitVector, 20); + assertTrue(parcBitVector_NumberOfBitsSet(parcBitVector) == 0, "parcBitVector_Clear failed"); + + parcBitVector_Release(&parcBitVector); +} + +LONGBOW_TEST_CASE(Global, parcBitVector_SetVector) +{ + PARCBitVector *parcBitVector = parcBitVector_Create(); + assertTrue(parcBitVector, "parcBitVector_Create returned a NULL pointer"); + PARCBitVector *setVector = parcBitVector_Create(); + parcBitVector_Set(parcBitVector, 1); + assertTrue(parcBitVector_NumberOfBitsSet(parcBitVector) == 1, "parcBitVector_Set failed"); + + parcBitVector_Set(setVector, 20); + parcBitVector_SetVector(parcBitVector, setVector); + assertTrue(parcBitVector_NumberOfBitsSet(parcBitVector) == 2, "parcBitVector_SetVector failed"); + assertTrue(parcBitVector_NextBitSet(parcBitVector, 0) == 1, "parcBitVector_Set failed to set bit 1"); + assertTrue(parcBitVector_NextBitSet(parcBitVector, 2) == 20, "parcBitVector_SetVector failed to set bit 20"); + + parcBitVector_Set(setVector, 10); + parcBitVector_SetVector(parcBitVector, setVector); + assertTrue(parcBitVector_NumberOfBitsSet(parcBitVector) == 3, "parcBitVector_SetVector failed"); + parcBitVector_Release(&parcBitVector); + parcBitVector_Release(&setVector); +} + +LONGBOW_TEST_CASE(Global, parcBitVector_Reset) +{ + PARCBitVector *parcBitVector = parcBitVector_Create(); + assertTrue(parcBitVector, "parcBitVector_Create returned a NULL pointer"); + + // Reset and empty vector test + parcBitVector_Reset(parcBitVector); + assertTrue(parcBitVector_NumberOfBitsSet(parcBitVector) == 0, "parcBitVector_Reset failed"); + + parcBitVector_Set(parcBitVector, 1); + parcBitVector_Set(parcBitVector, 42); + assertTrue(parcBitVector_NumberOfBitsSet(parcBitVector) == 2, "parcBitVector_Set failed"); + assertTrue(parcBitVector->bitLength == 48, "Expected a bitLength of 48"); + + parcBitVector_Reset(parcBitVector); + assertTrue(parcBitVector_NumberOfBitsSet(parcBitVector) == 0, "parcBitVector_Reset failed"); + assertTrue(parcBitVector->bitLength == 48, "Expected a bitLength of 48"); + + parcBitVector_Release(&parcBitVector); +} + +LONGBOW_TEST_CASE(Global, parcBitVector_ClearVector) +{ + PARCBitVector *parcBitVector = parcBitVector_Create(); + assertTrue(parcBitVector, "parcBitVector_Create returned a NULL pointer"); + + PARCBitVector *setVector = parcBitVector_Create(); + parcBitVector_Set(parcBitVector, 1); + assertTrue(parcBitVector_NumberOfBitsSet(parcBitVector) == 1, "parcBitVector_Set failed to set bit"); + + parcBitVector_Set(setVector, 1); + parcBitVector_Set(setVector, 20); + parcBitVector_ClearVector(parcBitVector, setVector); + assertTrue(parcBitVector_NumberOfBitsSet(parcBitVector) == 0, "parcBitVector_ClearVector failed to clear vector"); + + parcBitVector_Set(parcBitVector, 12); + parcBitVector_Set(parcBitVector, 17); + parcBitVector_ClearVector(parcBitVector, parcBitVector); + assertTrue(parcBitVector_NumberOfBitsSet(parcBitVector) == 0, "parcBitVector_ClearVector failed to clear vector"); + + parcBitVector_Release(&parcBitVector); + parcBitVector_Release(&setVector); +} + +LONGBOW_TEST_CASE(Global, parcBitVector_NextBitSet) +{ + PARCBitVector *parcBitVector = parcBitVector_Create(); + assertTrue(parcBitVector, "parcBitVector_Create returned a NULL pointer"); + assertTrue(parcBitVector_NumberOfBitsSet(parcBitVector) == 0, "parcBitVector_Create created a non-empty vector"); + + int nextBit = parcBitVector_NextBitSet(parcBitVector, 0); + assertTrue(nextBit == -1, "parcBitVector_NextBitSet should have failed (%d)", nextBit); + + parcBitVector_Set(parcBitVector, 10); + nextBit = parcBitVector_NextBitSet(parcBitVector, 0); + assertTrue(nextBit == 10, "parcBitVector_NextBitSet failed (%d)", nextBit); + + nextBit = parcBitVector_NextBitSet(parcBitVector, 20); + assertTrue(nextBit == -1, "parcBitVector_NextBitSet read past end of vector (%d)", nextBit); + + nextBit = parcBitVector_NextBitSet(parcBitVector, 10); + assertTrue(nextBit == 10, "parcBitVector_NextBitSet failed (%d)", nextBit); + + nextBit = parcBitVector_NextBitSet(parcBitVector, 11); + assertTrue(nextBit == -1, "parcBitVector_NextBitSet should have failed (%d)", nextBit); + + parcBitVector_Set(parcBitVector, 20); + nextBit = parcBitVector_NextBitSet(parcBitVector, 11); + assertTrue(nextBit == 20, "parcBitVector_NextBitSet failed (%d)", nextBit); + parcBitVector_Release(&parcBitVector); +} + +LONGBOW_TEST_CASE(Global, parcBitVector_Get) +{ + PARCBitVector *parcBitVector = parcBitVector_Create(); + assertTrue(parcBitVector, "parcBitVector_Create returned a NULL pointer"); + assertTrue(parcBitVector_NumberOfBitsSet(parcBitVector) == 0, "parcBitVector_Create created a non-empty vector"); + + parcBitVector_Set(parcBitVector, 10); + int bitValue = parcBitVector_Get(parcBitVector, 10); + assertTrue(bitValue == 1, "parcBitVector_Get returned wrong value (%d)", bitValue); + + bitValue = parcBitVector_Get(parcBitVector, 11); + assertTrue(bitValue == 0, "parcBitVector_Get returned wrong value (%d)", bitValue); + + bitValue = parcBitVector_Get(parcBitVector, 100); + assertTrue(bitValue == -1, "parcBitVector_NextBitSet should have failed (%d)", bitValue); + + parcBitVector_Release(&parcBitVector); +} + +LONGBOW_TEST_CASE(Global, parcBitVector_ToString) +{ + PARCBitVector *parcBitVector = parcBitVector_Create(); + assertTrue(parcBitVector, "parcBitVector_Create returned a NULL pointer"); + + char *string = parcBitVector_ToString(parcBitVector); + assertTrue(strcmp(string, "[ ]") == 0, "parcBitVector_ToString returned unexpected representation (%s != [ ])", string); + parcMemory_Deallocate(&string); + + parcBitVector_Set(parcBitVector, 10); + parcBitVector_Set(parcBitVector, 1); + string = parcBitVector_ToString(parcBitVector); + assertTrue(strcmp(string, "[ 1 10 ]") == 0, "parcBitVector_ToString returned unexpected representation (%s != [ 1 10 ])", string); + parcMemory_Deallocate(&string); + + parcBitVector_Release(&parcBitVector); +} + +LONGBOW_TEST_CASE(Global, parcBitVector_Copy) +{ + PARCBitVector *parcBitVector = parcBitVector_Create(); + assertTrue(parcBitVector, "parcBitVector_Create returned a NULL pointer"); + + parcBitVector_Set(parcBitVector, 10); + PARCBitVector *copy = parcBitVector_Copy(parcBitVector); + assertTrue(parcBitVector_NumberOfBitsSet(copy) == 1, "parcBitVector_Copy failed to copy set bit"); + assertTrue(parcBitVector_NextBitSet(copy, 0) == 10, "parcBitVector_Copy failed to copy correct bit"); + + parcBitVector_Release(©); + parcBitVector_Release(&parcBitVector); +} + +LONGBOW_TEST_CASE(Global, parcBitVector_Equals) +{ + PARCBitVector *parcBitVector = parcBitVector_Create(); + assertTrue(parcBitVector, "parcBitVector_Create returned a NULL pointer"); + + parcBitVector_Set(parcBitVector, 10); + PARCBitVector *copy = parcBitVector_Copy(parcBitVector); + assertTrue(parcBitVector_Equals(parcBitVector, copy), "Duplicate vector found unequal"); + + parcBitVector_Set(copy, 9); + assertFalse(parcBitVector_Equals(parcBitVector, copy), "Unequal vector found equal"); + + parcBitVector_Clear(copy, 9); + parcBitVector_Set(copy, 29); + assertFalse(parcBitVector_Equals(parcBitVector, copy), "Unequal long vector found equal"); + + parcBitVector_Clear(copy, 29); + assertTrue(parcBitVector_Equals(parcBitVector, copy), "Equal long vector found unequal"); + assertTrue(parcBitVector_Equals(copy, parcBitVector), "Equal long vector found unequal"); + + parcBitVector_Release(©); + parcBitVector_Release(&parcBitVector); +} + +LONGBOW_TEST_CASE(Global, parcBitVector_Contains) +{ + PARCBitVector *supersetVector = parcBitVector_Create(); + + parcBitVector_Set(supersetVector, 10); + parcBitVector_Set(supersetVector, 11); + + PARCBitVector *testVector = parcBitVector_Create(); + parcBitVector_Set(testVector, 10); + assertTrue(parcBitVector_Contains(supersetVector, testVector), "Expect superset to contain testVector"); + + parcBitVector_Set(testVector, 12); + assertFalse(parcBitVector_Contains(supersetVector, testVector), "Expect superset to not contain testVector"); + + parcBitVector_Release(&supersetVector); + parcBitVector_Release(&testVector); +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks %d memory allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_BitVector); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_Buffer.c b/libparc/parc/algol/test/test_parc_Buffer.c new file mode 100644 index 00000000..99b3bfd6 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_Buffer.c @@ -0,0 +1,1502 @@ +/* + * 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 <inttypes.h> +#include <stdio.h> +#include <inttypes.h> + +#include <LongBow/unit-test.h> +#include <LongBow/debugging.h> +#include <parc/algol/parc_SafeMemory.h> +#include <parc/testing/parc_ObjectTesting.h> + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../parc_Buffer.c" + +LONGBOW_TEST_RUNNER(parcBuffer) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(CreateDestroy); + LONGBOW_RUN_TEST_FIXTURE(Static); + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(GettersSetters); + LONGBOW_RUN_TEST_FIXTURE(CreateDestroyErrors); + LONGBOW_RUN_TEST_FIXTURE(Errors); + LONGBOW_RUN_TEST_FIXTURE(Performance); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parcBuffer) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parcBuffer) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(CreateDestroy) +{ + LONGBOW_RUN_TEST_CASE(CreateDestroy, parcBuffer_Allocate); + LONGBOW_RUN_TEST_CASE(CreateDestroy, parcBuffer_Allocate_0); + LONGBOW_RUN_TEST_CASE(CreateDestroy, parcBuffer_Allocate_AcquireRelease); + LONGBOW_RUN_TEST_CASE(CreateDestroy, parcBuffer_Allocate_SIZE_MAX); + LONGBOW_RUN_TEST_CASE(CreateDestroy, parcBuffer_Wrap); + LONGBOW_RUN_TEST_CASE(CreateDestroy, parcBuffer_Wrap_NULL); + LONGBOW_RUN_TEST_CASE(CreateDestroy, parcBuffer_Wrap_WithOffset); + LONGBOW_RUN_TEST_CASE(CreateDestroy, parcBuffer_AllocateCString); +} + +LONGBOW_TEST_FIXTURE_SETUP(CreateDestroy) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(CreateDestroy) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks %d memory allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(CreateDestroy, parcBuffer_Allocate) +{ + PARCBuffer *actual = parcBuffer_Allocate(10); + assertTrue(parcBuffer_Position(actual) == 0, "Expected initial position to be 0."); + assertTrue(parcBuffer_Limit(actual) == 10, "Expected initial limit to be 10."); + assertTrue(_markIsDiscarded(actual), "Expected initial mark to be discarded."); + + parcBuffer_Release(&actual); +} + +LONGBOW_TEST_CASE(CreateDestroy, parcBuffer_Allocate_0) +{ + PARCBuffer *actual = parcBuffer_Allocate(0); + assertTrue(parcBuffer_Position(actual) == 0, "Expected initial position to be 0."); + assertTrue(parcBuffer_Limit(actual) == 0, "Expected initial limit to be 10."); + assertTrue(_markIsDiscarded(actual), "Expected initial mark to be discarded."); + + parcBuffer_Release(&actual); +} + +LONGBOW_TEST_CASE(CreateDestroy, parcBuffer_Allocate_SIZE_MAX) +{ + PARCBuffer *actual = parcBuffer_Allocate(SIZE_MAX); + assertNull(actual, "Expected parcBuffer_Allocate to return NULL"); +} + +LONGBOW_TEST_CASE(CreateDestroy, parcBuffer_Wrap_NULL) +{ + PARCBuffer *actual = parcBuffer_Wrap(NULL, 10, 0, 10); + assertNull(actual, "Expected parcBuffer_Wrap to return NULL"); +} + +LONGBOW_TEST_CASE(CreateDestroy, parcBuffer_Wrap) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCBuffer *actual = parcBuffer_Wrap(array, 10, 0, 10); + assertTrue(parcBuffer_Position(actual) == 0, "Expected initial position to be 0."); + assertTrue(parcBuffer_Limit(actual) == sizeof(array) / sizeof(array[0]), "Expected initial limit to be 10."); + assertTrue(_markIsDiscarded(actual), "Expected initial mark to be discarded."); + + parcBuffer_Release(&actual); +} + +LONGBOW_TEST_CASE(CreateDestroy, parcBuffer_Wrap_WithOffset) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCBuffer *actual = parcBuffer_Wrap(array, 10, 3, 10); + assertTrue(parcBuffer_Capacity(actual) == 10, "Expected initial capacity to be 3."); + assertTrue(parcBuffer_Limit(actual) == 10, "Expected initial limit to be 3."); + assertTrue(parcBuffer_Position(actual) == 3, "Expected initial position to be 0."); + assertTrue(_markIsDiscarded(actual), "Expected initial mark to be discarded."); + + parcBuffer_Release(&actual); +} + +LONGBOW_TEST_CASE(CreateDestroy, parcBuffer_AllocateCString) +{ + PARCBuffer *buffer = parcBuffer_AllocateCString("Hello World"); + assertNotNull(buffer, "Expected parcBuffer_AllocateCString to return non-null value"); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(CreateDestroy, parcBuffer_Allocate_AcquireRelease) +{ + PARCBuffer *expected = parcBuffer_Allocate(10); + PARCBuffer *actual = parcBuffer_Acquire(expected); + + assertTrue(expected == actual, "Expected %p, actual %p", (void *) expected, (void *) actual); + + parcBuffer_Release(&expected); + assertTrue(expected == NULL, "Expected parcBuffer_Release to NULL the pointer."); + parcBuffer_Release(&actual); + assertTrue(actual == NULL, "Expected parcBuffer_Release to NULL the pointer."); +} + +LONGBOW_TEST_FIXTURE(CreateDestroyErrors) +{ + LONGBOW_RUN_TEST_CASE(CreateDestroyErrors, parcBuffer_Allocate_AcquireRelease_TooMany); + LONGBOW_RUN_TEST_CASE(CreateDestroyErrors, parcBuffer_WrapByteArray_limit_exceeds_capacity); +} + +LONGBOW_TEST_FIXTURE_SETUP(CreateDestroyErrors) +{ + PARCByteArray *array = parcByteArray_Allocate(10); + longBowTestCase_SetClipBoardData(testCase, array); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(CreateDestroyErrors) +{ + PARCByteArray *array = longBowTestCase_GetClipBoardData(testCase); + parcByteArray_Release(&array); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks %d memory allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE_EXPECTS(CreateDestroyErrors, parcBuffer_Allocate_AcquireRelease_TooMany, .event = &LongBowTrapIllegalValue) +{ + PARCBuffer *expected = parcBuffer_Allocate(10); + PARCBuffer *actual = parcBuffer_Acquire(expected); + PARCBuffer *alias = actual; + + parcBuffer_Release(&expected); + parcBuffer_Release(&actual); + parcBuffer_Release(&alias); // this must fail. +} + +LONGBOW_TEST_CASE_EXPECTS(CreateDestroyErrors, parcBuffer_WrapByteArray_limit_exceeds_capacity, .event = &LongBowAssertEvent) +{ + PARCByteArray *array = longBowTestCase_GetClipBoardData(testCase); + + PARCBuffer *buffer = parcBuffer_WrapByteArray(array, 0, parcByteArray_Capacity(array) + 1); + + assertNotNull(buffer, "Expected NULL"); +} + +LONGBOW_TEST_FIXTURE(Global) +{ +// LONGBOW_RUN_TEST_CASE(Global, HELPME); +// LONGBOW_RUN_TEST_CASE(Global, HELPME2); + + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_Array); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_ArrayOffset); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_Clear); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_Clone); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_Duplicate); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_Clone_WithOffset); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_Compare); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_Equals); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_Equals_ZeroLength); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_Equals_Bug80); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_Flip); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_GetByte); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_GetBytes); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_GetBytes_Incremental); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_HasRemaining); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_HashCode); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_HashCode_ZeroRemaining); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_Mark); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_Resize_Growing); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_Resize_Growing_AtLimit); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_Resize_Shrinking); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_Resize_Shrinking_AtLimit); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_Resize_Example); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_Resize_Slice); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_Overlay); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_Position); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_PutBuffer); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_PutBuffer_ZeroLength_operand); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_PutByte); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_PutBytes); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_PutIndex); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_PutUint16); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_PutCString); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_Remaining); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_Rewind); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_SetLimit); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_SetLimit_TruncatePosition); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_SetPosition); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_Slice); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_ToString); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_ToString_ZeroRemaining); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_SkipOver); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_SkipOver_NotFound); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_SkipTo_NotFound); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_SkipTo); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_FindUint8); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_FindUint8_NotFound); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_IsValid_True); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_ParseNumeric_Decimal); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_ParseNumeric_Hexadecimal); + + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_ParseHexString); + LONGBOW_RUN_TEST_CASE(Global, parcBuffer_CreateFromArray); +} + +static size_t _longBowGlobal_Global_outstanding; + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + _longBowGlobal_Global_outstanding = parcMemory_Outstanding(); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + LongBowStatus result = LONGBOW_STATUS_SUCCEEDED; + + size_t allocationsLeaked = parcMemory_Outstanding() - _longBowGlobal_Global_outstanding; + + if (allocationsLeaked) { + printf("%s leaks memory by %zd allocations\n", longBowTestCase_GetName(testCase), allocationsLeaked); + parcSafeMemory_ReportAllocation(STDERR_FILENO); + result = LONGBOW_STATUS_MEMORYLEAK; + } + return result; +} + +LONGBOW_TEST_CASE(Global, HELPME) +{ + uint8_t decodeBytes[] = { 0x00, 0x02, 0x00, 0x0A, 'b', 'r', 'a', 'n', 'd', 'y', 'w', 'i', 'n', 'e' }; + PARCBuffer *b1 = parcBuffer_Wrap(decodeBytes, sizeof(decodeBytes), 0, sizeof(decodeBytes)); + + // b1 is a buffer wrapping a byte array. + // This will take 2 allocations: 1 for the buffer and 1 for the wrapper around the byte array. + + PARCBuffer *s2 = parcBuffer_Slice(b1); + + // s2 is another buffer referencing the wrapper created in the original buffer. + // This will increase the allocations by 1 for the buffer making it 3. + + // **** DO NOT RELEASE s2 + + parcBuffer_Release(&b1); + // This releases the b1 buffer, deallocating it. The wrapper around the original byte array still has a reference to it from s2. + // The number of allocations is reduced by 1, making it 2 (1 for s2, and 1 for the wrapper it references) + + assertTrue(parcMemory_Outstanding() == 2, "memory imbalance"); + + parcBuffer_Release(&s2); + + assertTrue(parcMemory_Outstanding() == 0, "memory imbalance must be 0, actual %d", parcMemory_Outstanding()); +} + +LONGBOW_TEST_CASE(Global, HELPME2) +{ + uint8_t decodeBytes[] = { 0x00, 0x02, 0x00, 0x0A, 'b', 'r', 'a', 'n', 'd', 'y', 'w', 'i', 'n', 'e' }; + PARCBuffer *b1 = parcBuffer_Allocate(sizeof(decodeBytes)); + // This will create a buffer, a wrapper, and an allocated array of bytes to wrap. + // The number of allocations is 3. + + parcBuffer_PutArray(b1, sizeof(decodeBytes), decodeBytes); + parcBuffer_Flip(b1); + + PARCBuffer *s2 = parcBuffer_Slice(b1); + // The number of allocations is 4. + + // **** DO NOT RELEASE s2 + + parcBuffer_Release(&b1); + // The number of allocations is now 3, the slice buffer, the wrapper, and the allocated array of bytes. + + // This will now correctly assert + assertTrue(parcMemory_Outstanding() == 3, "memory imbalance"); + + parcBuffer_Release(&s2); + assertTrue(parcMemory_Outstanding() == 0, "memory imbalance"); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_Equals) +{ + PARCBuffer *x = parcBuffer_Wrap((uint8_t [10]) { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 10, 0, 10); + PARCBuffer *y = parcBuffer_Wrap((uint8_t [10]) { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 10, 0, 10); + PARCBuffer *z = parcBuffer_Wrap((uint8_t [10]) { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 10, 0, 10); + + // _Pragma("GCC diagnostic push") + // _Pragma("GCC diagnostic ignored \"-Wzero-length-array\"") + // _Pragma("GCC diagnostic ignored \"-Wgnu-empty-initializer\"") + struct hack { + uint8_t dummy; + uint8_t empty[]; + }; + struct hack h = { 0 }; + PARCBuffer *u0 = parcBuffer_Wrap(h.empty, 0, 0, 0); + // _Pragma("GCC diagnostic pop") + + PARCBuffer *u1 = parcBuffer_Wrap((uint8_t [10]) { 0, 1, 2, 3, 4, 5, 6, 7, 8, 10 }, 10, 0, 10); + PARCBuffer *u2 = parcBuffer_Wrap((uint8_t [10]) { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 9, 0, 9); + PARCBuffer *u3 = parcBuffer_Wrap((uint8_t [9]) { 0, 1, 2, 3, 4, 5, 6, 7, 8 }, 9, 0, 9); + PARCBuffer *u4 = parcBuffer_SetPosition(parcBuffer_Wrap((uint8_t [9]) { 0, 1, 2, 3, 4, 5, 6, 7, 8 }, 9, 0, 9), 2); + PARCBuffer *u5 = parcBuffer_SetPosition(parcBuffer_Wrap((uint8_t [9]) { 0, 1, 2, 3, 4, 5, 6, 7, 8 }, 9, 0, 9), 9); + PARCBuffer *u6 = parcBuffer_SetPosition(parcBuffer_Wrap((uint8_t [9]) { 0, 1, 2, 3, 4, 5, 6, 7, 8 }, 9, 0, 9), 9); + PARCBuffer *u7 = parcBuffer_Wrap((uint8_t [9]) { 0 }, 0, 0, 0); + + parcObjectTesting_AssertEqualsFunction(parcBuffer_Equals, x, y, z, u0, u1, u2, u3, u4, u5, u6, u7, NULL); + + parcBuffer_Release(&x); + parcBuffer_Release(&y); + parcBuffer_Release(&z); + parcBuffer_Release(&u0); + parcBuffer_Release(&u1); + parcBuffer_Release(&u2); + parcBuffer_Release(&u3); + parcBuffer_Release(&u4); + parcBuffer_Release(&u5); + parcBuffer_Release(&u6); + parcBuffer_Release(&u7); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_Equals_ZeroLength) +{ + // _Pragma("GCC diagnostic push") + // _Pragma("GCC diagnostic ignored \"-Wzero-length-array\"") + // _Pragma("GCC diagnostic ignored \"-Wgnu-empty-initializer\"") + + struct hack { + uint8_t dummy; + uint8_t empty[]; + }; + struct hack h = { 0 }; + PARCBuffer *x = parcBuffer_Wrap(h.empty, 0, 0, 0); + PARCBuffer *y = parcBuffer_Wrap(h.empty, 0, 0, 0); + PARCBuffer *z = parcBuffer_Wrap(h.empty, 0, 0, 0); + + // _Pragma("GCC diagnostic pop") + + PARCBuffer *u1 = parcBuffer_Wrap((uint8_t [10]) { 0, 1, 2, 3, 4, 5, 6, 7, 8, 10 }, 10, 0, 10); + PARCBuffer *u2 = parcBuffer_Wrap((uint8_t [10]) { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 9, 0, 9); + + parcObjectTesting_AssertEqualsFunction(parcBuffer_Equals, x, y, z, u1, u2, NULL); + + parcBuffer_Release(&x); + parcBuffer_Release(&y); + parcBuffer_Release(&z); + parcBuffer_Release(&u1); + parcBuffer_Release(&u2); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_Equals_Bug80) +{ + PARCBuffer *x = parcBuffer_WrapCString("a"); + PARCBuffer *y = parcBuffer_WrapCString("a"); + PARCBuffer *z = parcBuffer_WrapCString("a"); + PARCBuffer *u1 = parcBuffer_WrapCString("b"); + PARCBuffer *u2 = parcBuffer_WrapCString(""); + PARCBuffer *u3 = parcBuffer_WrapCString("ab"); + + parcObjectTesting_AssertEqualsFunction(parcBuffer_Equals, x, y, z, u1, u2, u3); + + parcBuffer_Release(&x); + parcBuffer_Release(&y); + parcBuffer_Release(&z); + parcBuffer_Release(&u1); + parcBuffer_Release(&u2); + parcBuffer_Release(&u3); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_Compare) +{ + PARCBuffer *x = parcBuffer_Wrap((uint8_t [10]) { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 10, 0, 10); + PARCBuffer *y = parcBuffer_Wrap((uint8_t [10]) { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 10, 0, 10); + + PARCBuffer *equivalent[] = { + x, + y, + NULL + }; + PARCBuffer *lesser[] = { + parcBuffer_Wrap((uint8_t [9]) { 0 }, 0, 0, 0), + parcBuffer_Wrap((uint8_t [10]) { 0, 1, 2, 3, 4, 5, 6, 7, 8, 8}, 10, 0, 10), + parcBuffer_Wrap((uint8_t [9]) { 0, 1, 2, 3, 4, 5, 5, 7, 8,}, 9, 0, 9), + NULL + }; + PARCBuffer *greater[] = { + parcBuffer_Wrap((uint8_t [10]) { 0, 1, 2, 3, 4, 5, 6, 7, 8, 10 }, 10, 0, 10), + parcBuffer_Wrap((uint8_t [11]) { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 11, 0, 11), + NULL + }; + + parcObjectTesting_AssertCompareTo(parcBuffer_Compare, x, equivalent, lesser, greater); + + parcBuffer_Release(&x); + parcBuffer_Release(&y); + + for (int i = 0; lesser[i] != NULL; i++) { + parcBuffer_Release(&lesser[i]); + } + for (int i = 0; greater[i] != NULL; i++) { + parcBuffer_Release(&greater[i]); + } +} + +LONGBOW_TEST_CASE(Global, parcBuffer_Array) +{ + uint8_t expected[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCBuffer *buffer = parcBuffer_Wrap(expected, 10, 0, 10); + + PARCByteArray *array = parcBuffer_Array(buffer); + uint8_t *actual = parcByteArray_Array(array); + + parcBuffer_Release(&buffer); + + assertTrue(expected == actual, + "Expected %p, actual %p", (void *) expected, (void *) actual); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_Resize_Growing) +{ + uint8_t expected[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + PARCBuffer *buffer = parcBuffer_Allocate(12); + parcBuffer_PutArray(buffer, sizeof(expected), expected); + + parcBuffer_SetPosition(buffer, 5); + parcBuffer_SetLimit(buffer, 11); + parcBuffer_Mark(buffer); + + parcBuffer_Resize(buffer, 20); + + assertTrue(buffer->position == 5, "Expected position at %d, actual %zd", 5, buffer->position); + assertTrue(buffer->mark == 5, "Expected mark at %d, actual %zd", 5, buffer->mark); + assertTrue(buffer->limit == 11, "Expected limit at %d, actual %zd", 11, buffer->limit); + assertTrue(parcBuffer_Capacity(buffer) == 20, "Expected capacity at %d, actual %zd", 20, parcBuffer_Capacity(buffer)); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_Resize_Growing_AtLimit) +{ + uint8_t expected[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + PARCBuffer *buffer = parcBuffer_Allocate(12); + parcBuffer_PutArray(buffer, sizeof(expected), expected); + + parcBuffer_SetPosition(buffer, 5); + parcBuffer_Mark(buffer); + + parcBuffer_Resize(buffer, 20); + + assertTrue(buffer->position == 5, "Expected position at %d, actual %zd", 5, buffer->position); + assertTrue(buffer->mark == 5, "Expected mark at %d, actual %zd", 5, buffer->mark); + assertTrue(buffer->limit == 20, "Expected limit at %d, actual %zd", 20, buffer->limit); + assertTrue(parcBuffer_Capacity(buffer) == 20, "Expected capacity at %d, actual %zd", 20, parcBuffer_Capacity(buffer)); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_Resize_Shrinking) +{ + uint8_t expected[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + PARCBuffer *buffer = parcBuffer_Allocate(10); + parcBuffer_PutArray(buffer, sizeof(expected), expected); + + parcBuffer_SetPosition(buffer, 3); + parcBuffer_SetLimit(buffer, 4); + parcBuffer_Mark(buffer); + + parcBuffer_Resize(buffer, 5); + + assertTrue(buffer->position == 3, "Expected position at %d, actual %zd", 3, buffer->position); + assertTrue(buffer->mark == 3, "Expected mark to be 3"); + assertTrue(buffer->limit == 4, "Expected limit at %d, actual %zd", 4, buffer->limit); + assertTrue(parcBuffer_Capacity(buffer) == 5, "Expected capacity at %d, actual %zd", 5, parcBuffer_Capacity(buffer)); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_Resize_Shrinking_AtLimit) +{ + uint8_t expected[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + PARCBuffer *buffer = parcBuffer_Allocate(10); + parcBuffer_PutArray(buffer, sizeof(expected), expected); + + parcBuffer_SetPosition(buffer, 5); + parcBuffer_SetLimit(buffer, 5); + parcBuffer_Mark(buffer); + + parcBuffer_Resize(buffer, 3); + + assertTrue(buffer->position == 3, "Expected position at %d, actual %zd", 3, buffer->position); + assertTrue(_markIsDiscarded(buffer), "Expected mark to be discarded"); + assertTrue(buffer->limit == 3, "Expected limit at %d, actual %zd", 3, buffer->limit); + assertTrue(parcBuffer_Capacity(buffer) == 3, "Expected capacity at %d, actual %zd", 3, parcBuffer_Capacity(buffer)); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_Resize_Example) +{ + PARCBuffer *buffer = parcBuffer_WrapCString("Hello World"); + parcBuffer_Resize(buffer, 4); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_Resize_Slice) +{ + PARCBuffer *buffer = parcBuffer_WrapCString("Hello World"); + parcBuffer_SetPosition(buffer, 5); + PARCBuffer *slice = parcBuffer_Slice(buffer); + + parcBuffer_Resize(slice, 4); + + parcBuffer_Release(&buffer); + parcBuffer_Release(&slice); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_Flip) +{ + uint8_t expected[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCBuffer *buffer = parcBuffer_Allocate(10); + parcBuffer_PutArray(buffer, 10, expected); + parcBuffer_Flip(buffer); + assertTrue(parcBuffer_Position(buffer) == 0, "Expected position to be 0."); + assertTrue(parcBuffer_Limit(buffer) == 10, "Expected limit to be 10."); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_Clear) +{ + uint8_t expected[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCBuffer *buffer = parcBuffer_Allocate(10); + parcBuffer_PutArray(buffer, 10, expected); + parcBuffer_Clear(buffer); + assertTrue(parcBuffer_Position(buffer) == 0, "Expected position to be 0."); + assertTrue(parcBuffer_Limit(buffer) == 10, "Expected limit to be 10."); + assertTrue(buffer->mark >= parcBuffer_Capacity(buffer), "Expected the mark to be unset."); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_ArrayOffset) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + size_t expected = 5; + PARCBuffer *buffer = parcBuffer_Wrap(array, 10, expected, 10); + + size_t actual = parcBuffer_ArrayOffset(buffer); + parcBuffer_Release(&buffer); + + assertTrue(0 == actual, + "Expected offset to be 0, actual %zd", actual); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_Position) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCBuffer *buffer = parcBuffer_Wrap(array, 10, 0, 10); + + size_t expected = 5; + parcBuffer_SetPosition(buffer, expected); + + size_t actual = parcBuffer_Position(buffer); + + assertTrue(expected == actual, + "Expected position to be 0, actual %zd", actual); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_Overlay) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + uint8_t expected[5] = { 5, 6, 7, 8, 9 }; + + PARCBuffer *buffer = parcBuffer_Wrap(array, 10, 0, 10); + + size_t position = 5; + parcBuffer_SetPosition(buffer, position); + uint8_t *actual = parcBuffer_Overlay(buffer, sizeof(array) - position); + + assertTrue(memcmp(expected, actual, sizeof(expected)) == 0, + "Array contents should not be different."); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_Clone) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCBuffer *original = parcBuffer_Wrap(array, 10, 0, 10); + + PARCBuffer *clone = parcBuffer_Copy(original); + + assertTrue(clone != original, "Expected the clone to be a different instance."); + + assertTrue(parcBuffer_Equals(original, clone), "Expected clone to be equal to the original."); + + parcBuffer_Release(&original); + assertNull(original, "Expected the parcBuffer_Release function to NULL the pointer."); + + parcBuffer_Release(&clone); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_Clone_WithOffset) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCBuffer *original = parcBuffer_Wrap(array, 10, 0, 10); + parcBuffer_SetLimit(original, 9); + parcBuffer_SetPosition(original, 1); + PARCBuffer *range = parcBuffer_Slice(original); + + PARCBuffer *clone = parcBuffer_Copy(range); + + assertTrue(clone != original, "Expected the clone to be a different instance."); + + assertTrue(parcBuffer_Equals(range, clone), "Expected clone to be equal to the original."); + + parcBuffer_Release(&clone); + parcBuffer_Release(&range); + parcBuffer_Release(&original); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_SetPosition) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCBuffer *buffer = parcBuffer_Wrap(array, 10, 0, 10); + + size_t expected = 2; + parcBuffer_SetPosition(buffer, expected); + size_t actual = parcBuffer_Position(buffer); + + assertTrue(expected == actual, "Expected %zd, actual %zd", expected, actual); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_SetLimit) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCBuffer *buffer = parcBuffer_Wrap(array, 10, 0, 10); + + size_t expected = 2; + parcBuffer_SetLimit(buffer, expected); + size_t actual = parcBuffer_Limit(buffer); + + assertTrue(expected == actual, "Expected %zd, actual %zd", expected, actual); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_SetLimit_TruncatePosition) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCBuffer *buffer = parcBuffer_Wrap(array, 10, 0, 10); + + parcBuffer_SetPosition(buffer, 5); + parcBuffer_Mark(buffer); + + size_t expected = 2; + parcBuffer_SetLimit(buffer, expected); + size_t actual = parcBuffer_Limit(buffer); + + assertTrue(expected == actual, "Expected %zd, actual %zd", expected, actual); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_Slice) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCBuffer *buffer = parcBuffer_Wrap(array, 10, 0, 10); + parcBuffer_GetUint8(buffer); + + PARCBuffer *actual = parcBuffer_Slice(buffer); + assertTrue(parcBuffer_Position(actual) == 0, + "Expected position to be 0"); + assertTrue(parcBuffer_Limit(actual) == parcBuffer_Remaining(buffer), + "Expected position to be %zd", parcBuffer_Remaining(buffer)); + assertTrue(_markIsDiscarded(actual), "Expected the mark to be discarded."); + + parcBuffer_Release(&buffer); + parcBuffer_Release(&actual); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_Remaining) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCBuffer *buffer = parcBuffer_Wrap(array, 10, 0, 10); + + size_t expected = 2; + parcBuffer_SetLimit(buffer, expected); + size_t actual = parcBuffer_Limit(buffer); + + assertTrue(expected == actual, "Expected %zd, actual %zd", expected, actual); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_HasRemaining) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCBuffer *buffer = parcBuffer_Wrap(array, 10, 0, 10); + bool actual = parcBuffer_HasRemaining(buffer); + + assertTrue(actual, "Expected true"); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_Rewind) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCBuffer *buffer = parcBuffer_Wrap(array, 10, 0, 10); + parcBuffer_SetPosition(buffer, 4); + size_t actual = parcBuffer_Position(buffer); + assertTrue(actual == 4, "Expected position to be at 4."); + + parcBuffer_Rewind(buffer); + + actual = parcBuffer_Position(buffer); + assertTrue(actual == 0, "Expected position to be at 0."); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_Duplicate) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCBuffer *buffer = parcBuffer_Wrap(array, 10, 0, 10); + parcBuffer_SetPosition(buffer, 4); + + PARCBuffer *buffer2 = parcBuffer_Duplicate(buffer); + + assertTrue(buffer != buffer2, "Expected distinct pointers to the different buffers."); + assertTrue(parcBuffer_Position(buffer) == parcBuffer_Position(buffer2), "Expected equal position values."); + assertTrue(parcBuffer_Limit(buffer) == parcBuffer_Limit(buffer2), "Expected equal limit values."); + assertTrue(parcBuffer_Capacity(buffer) == parcBuffer_Capacity(buffer2), "Expected equal capacity values."); + + parcBuffer_Rewind(buffer); + assertFalse(parcBuffer_Position(buffer) == parcBuffer_Position(buffer2), "Expected unequal position values."); + + parcBuffer_Release(&buffer); + parcBuffer_Release(&buffer2); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_Mark) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + PARCBuffer *buffer = parcBuffer_Wrap(array, 10, 0, 10); + + size_t expected = 2; + parcBuffer_SetPosition(buffer, expected); + parcBuffer_Mark(buffer); + parcBuffer_SetPosition(buffer, 4); + parcBuffer_Reset(buffer); + size_t actual = parcBuffer_Position(buffer); + + assertTrue(expected == actual, "Expected %zd, actual %zd", expected, actual); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_PutByte) +{ + PARCBuffer *buffer = parcBuffer_Allocate(10); + + uint8_t expectedValue = 1; + parcBuffer_PutUint8(buffer, expectedValue); + + size_t expectedPosition = 1; + size_t actualPosition = parcBuffer_Position(buffer); + + parcBuffer_SetPosition(buffer, 0); + uint8_t actualValue = parcBuffer_GetAtIndex(buffer, 0); + parcBuffer_Release(&buffer); + + assertTrue(expectedValue == actualValue, + "Expected %d, actual %d", expectedValue, actualValue); + assertTrue(expectedPosition == actualPosition, + "Expected %zu, actual %zu", expectedPosition, actualPosition); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_PutCString) +{ + PARCBuffer *buffer = parcBuffer_Allocate(10); + + char *expectedValue = "abcdefg"; + parcBuffer_PutCString(buffer, expectedValue); + + size_t expectedPosition = 8; + size_t actualPosition = parcBuffer_Position(buffer); + + uint8_t zero = parcBuffer_GetAtIndex(buffer, 7); + + assertTrue(zero == 0, "Expected zero, actual %d", zero); + + assertTrue(expectedPosition == actualPosition, + "Expected %zu, actual %zu", expectedPosition, actualPosition); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_PutUint16) +{ + PARCBuffer *buffer = parcBuffer_Allocate(10); + + uint16_t expectedValue = 0x1234; + parcBuffer_PutUint16(buffer, expectedValue); + + size_t expectedPosition = 2; + size_t actualPosition = parcBuffer_Position(buffer); + assertTrue(expectedPosition == actualPosition, + "Expected position %zd, actual %zd", expectedPosition, actualPosition); + + parcBuffer_Flip(buffer); + uint16_t actualValue = parcBuffer_GetUint16(buffer); + + actualPosition = parcBuffer_Position(buffer); + + parcBuffer_Release(&buffer); + + assertTrue(expectedValue == actualValue, + "Expected %d, actual %d", expectedValue, actualValue); + assertTrue(expectedPosition == actualPosition, + "Expected %zu, actual %zu", expectedPosition, actualPosition); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_PutIndex) +{ + PARCBuffer *buffer = parcBuffer_Allocate(10); + + uint8_t expected = 1; + parcBuffer_PutAtIndex(buffer, 0, expected); + uint8_t actual = parcBuffer_GetAtIndex(buffer, 0); + + parcBuffer_Release(&buffer); + + assertTrue(expected == actual, + "Expected %" PRIu8 ", actual %" PRIu8 "", expected, actual); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_PutBytes) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCBuffer *buffer = parcBuffer_Allocate(10); + parcBuffer_PutArray(buffer, 10, array); + + size_t expected = parcBuffer_Limit(buffer); + size_t actual = parcBuffer_Position(buffer); + + assertTrue(expected == actual, "Expected position to be at the limit."); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_PutBuffer) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCBuffer *buffer1 = parcBuffer_Allocate(10); + parcBuffer_PutArray(buffer1, 5, array); + + PARCBuffer *buffer2 = parcBuffer_Allocate(10); + parcBuffer_PutArray(buffer2, 5, &array[5]); + parcBuffer_Flip(buffer2); + + parcBuffer_PutBuffer(buffer1, buffer2); + + size_t expected = parcBuffer_Limit(buffer1); + size_t actual = parcBuffer_Position(buffer1); + + assertTrue(expected == actual, "Expected position to be at the limit. Expected %zd, actual %zd", expected, actual); + assertTrue(memcmp(array, parcByteArray_Array(buffer1->array), sizeof(array)) == 0, + "Array content differs."); + + parcBuffer_Release(&buffer1); + parcBuffer_Release(&buffer2); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_GetByte) +{ + uint8_t expected[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCBuffer *buffer = parcBuffer_Allocate(10); + parcBuffer_PutArray(buffer, sizeof(expected), expected); + parcBuffer_Flip(buffer); + + uint8_t actual = parcBuffer_GetUint8(buffer); + + assertTrue(expected[0] == actual, + "Expected %d, actual %d", expected[0], actual); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_GetBytes) +{ + uint8_t expected[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + uint8_t actual[10]; + + PARCBuffer *buffer = parcBuffer_Allocate(10); + parcBuffer_PutArray(buffer, sizeof(expected), expected); + parcBuffer_Flip(buffer); + + parcBuffer_GetBytes(buffer, sizeof(actual), actual); + + assertTrue(memcmp(expected, actual, sizeof(actual)) == 0, + "Expected arrays to be equal."); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_GetBytes_Incremental) +{ + uint8_t expected[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + uint8_t actual[10]; + + PARCBuffer *buffer = parcBuffer_Allocate(10); + parcBuffer_PutArray(buffer, sizeof(expected), expected); + parcBuffer_Flip(buffer); + + parcBuffer_GetBytes(buffer, 1, actual); + assertTrue(parcBuffer_Position(buffer) == 1, "Expected position to be %d\n", 1); + assertTrue(actual[0] == expected[0], "Expected %d, actual %d", expected[0], actual[0]); + parcBuffer_GetBytes(buffer, 1, actual); + assertTrue(parcBuffer_Position(buffer) == 2, "Expected position to be 2, actual %zd\n", parcBuffer_Position(buffer)); + assertTrue(actual[0] == expected[1], "Expected %d, actual %d", expected[1], actual[0]); + parcBuffer_GetBytes(buffer, 1, actual); + assertTrue(parcBuffer_Position(buffer) == 3, "Expected position to be 3, actual %zd\n", parcBuffer_Position(buffer)); + assertTrue(actual[0] == expected[2], "Expected %d, actual %d", expected[2], actual[0]); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_PutBuffer_ZeroLength_operand) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCBuffer *buffer1 = parcBuffer_Allocate(10); + parcBuffer_PutArray(buffer1, 10, array); + + PARCBuffer *buffer2 = parcBuffer_Allocate(0); + parcBuffer_PutBuffer(buffer1, buffer2); + + size_t expected = parcBuffer_Limit(buffer1); + size_t actual = parcBuffer_Position(buffer1); + + assertTrue(expected == actual, "Expected position to be at the limit."); + + parcBuffer_Release(&buffer1); + parcBuffer_Release(&buffer2); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_HashCode) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCBuffer *buffer1 = parcBuffer_Allocate(10); + parcBuffer_PutArray(buffer1, 10, array); + parcBuffer_Flip(buffer1); + + PARCBuffer *buffer2 = parcBuffer_Allocate(10); + parcBuffer_PutArray(buffer2, 10, array); + parcBuffer_Flip(buffer2); + + PARCHashCode hashX = parcBuffer_HashCode(buffer1); + PARCHashCode hashY = parcBuffer_HashCode(buffer2); + + assertTrue(hashX == hashY, "Expected %" PRIPARCHashCode ", actual %" PRIPARCHashCode, hashX, hashY); + + parcBuffer_Release(&buffer1); + parcBuffer_Release(&buffer2); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_HashCode_ZeroRemaining) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCBuffer *buffer1 = parcBuffer_Allocate(10); + parcBuffer_PutArray(buffer1, 10, array); + + PARCHashCode hashX = parcBuffer_HashCode(buffer1); + + assertTrue(hashX == 0, "Expected 0, actual %" PRIPARCHashCode, hashX); + + parcBuffer_Release(&buffer1); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_ToString) +{ + uint8_t array[] = { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', 'x' }; + + PARCBuffer *buffer = parcBuffer_Allocate(sizeof(array) - 1); + parcBuffer_PutArray(buffer, sizeof(array) - 1, array); + parcBuffer_Flip(buffer); + + char *actual = parcBuffer_ToString(buffer); + + assertTrue(strcmp("hello world", actual) == 0, "Expected 'hello world', actual %s", actual); + + parcMemory_Deallocate((void **) &actual); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_ToString_ZeroRemaining) +{ + uint8_t array[] = { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', 'x' }; + + PARCBuffer *buffer = parcBuffer_Allocate(sizeof(array) - 1); + parcBuffer_PutArray(buffer, sizeof(array) - 1, array); +// parcBuffer_Flip(buffer); + + char *actual = parcBuffer_ToString(buffer); + + assertTrue(strcmp("", actual) == 0, "Expected '', actual %s", actual); + + parcMemory_Deallocate((void **) &actual); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_SkipOver) +{ + PARCBuffer *buffer = parcBuffer_WrapCString("Hello World"); + uint8_t skipOverBytes[] = { 'H', 0 }; + + bool actual = parcBuffer_SkipOver(buffer, 1, skipOverBytes); + + assertTrue(actual, "Expected parcBuffer_SkipOver to return true."); + + uint8_t peekByte = parcBuffer_PeekByte(buffer); + + assertTrue(peekByte == 'e', "Expected buffer to point to 'e', actual '%c'", peekByte); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_SkipOver_NotFound) +{ + PARCBuffer *buffer = parcBuffer_WrapCString("Hello World"); + + bool actual = parcBuffer_SkipOver(buffer, 8, (uint8_t *) "Helo Wrd"); + + assertFalse(actual, "Expected parcBuffer_SkipOver to return false."); + + assertTrue(parcBuffer_Remaining(buffer) == 0, + "Expected buffer to have no remaining bytes. Actual %zd", parcBuffer_Remaining(buffer)); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_SkipTo) +{ + PARCBuffer *buffer = parcBuffer_WrapCString("Hello World"); + uint8_t skipToBytes[] = { 'l', 0 }; + + bool actual = parcBuffer_SkipTo(buffer, 1, skipToBytes); + + assertTrue(actual, "Expected parcBuffer_SkipOver to return true."); + + uint8_t peekByte = parcBuffer_PeekByte(buffer); + + assertTrue(peekByte == 'l', "Expected buffer to point to 'l', actual '%c'", peekByte); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_SkipTo_NotFound) +{ + PARCBuffer *buffer = parcBuffer_WrapCString("Hello World"); + + bool actual = parcBuffer_SkipTo(buffer, 1, (uint8_t *) "x"); + + assertFalse(actual, "Expected parcBuffer_SkipOver to return false."); + assertTrue(parcBuffer_Remaining(buffer) == 0, + "Expected buffer to have no remaining bytes. Actual %zd", parcBuffer_Remaining(buffer)); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_FindUint8) +{ + PARCBuffer *buffer = parcBuffer_WrapCString("Hello World"); + size_t index = parcBuffer_FindUint8(buffer, 'e'); + assertTrue(index == 1, "Expected index to be 1, actual %zu", index); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_FindUint8_NotFound) +{ + PARCBuffer *buffer = parcBuffer_WrapCString("Hello World"); + size_t index = parcBuffer_FindUint8(buffer, 'z'); + assertTrue(index == SIZE_MAX, "Expected index to be SIZE_MAX, actual %zu", index); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_IsValid_True) +{ + PARCBuffer *buffer = parcBuffer_WrapCString("Hello World"); + bool actual = parcBuffer_IsValid(buffer); + assertTrue(actual, "Expected PARCBuffer to be valid"); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_ParseNumeric_Decimal) +{ + PARCBuffer *buffer = parcBuffer_WrapCString("123abc"); + + uint64_t actual = parcBuffer_ParseNumeric(buffer); + + assertTrue(actual == 123, "Expected 123, actual %" PRIu64 "", actual); + assertTrue(parcBuffer_Position(buffer) == 3, "Expected position to be 3, actual %zd", parcBuffer_Position(buffer)); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_ParseNumeric_Hexadecimal) +{ + PARCBuffer *buffer = parcBuffer_WrapCString("0x123xyz"); + + uint64_t actual = parcBuffer_ParseNumeric(buffer); + + assertTrue(actual == 0x123, "Expected 0x123, actual %" PRIx64 "", actual); + assertTrue(parcBuffer_Position(buffer) == 5, "Expected position to be 5, actual %zd", parcBuffer_Position(buffer)); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_ParseHexString) +{ + char *expected = "00"; + PARCBuffer *buffer = parcBuffer_ParseHexString("3030"); + parcBuffer_Flip(buffer); + char *actual = parcBuffer_ToString(buffer); + + assertTrue(strcmp(expected, actual) == 0, "Expected %s, actual %s", expected, actual); + + parcMemory_Deallocate(&actual); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcBuffer_CreateFromArray) +{ + char *expected = "0123456789ABCDEF"; + PARCBuffer *buffer = parcBuffer_CreateFromArray(expected, strlen(expected)); + + assertTrue(parcBuffer_Position(buffer) == 16, "Expected position to be at 15, actual %zd", parcBuffer_Position(buffer)); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_FIXTURE(GettersSetters) +{ + LONGBOW_RUN_TEST_CASE(GettersSetters, parcPutGetUint8); + LONGBOW_RUN_TEST_CASE(GettersSetters, parcPutGetUint16); + LONGBOW_RUN_TEST_CASE(GettersSetters, parcPutGetUint32); + LONGBOW_RUN_TEST_CASE(GettersSetters, parcPutGetUint64); + LONGBOW_RUN_TEST_CASE(GettersSetters, parcBuffer_ToHexString); + LONGBOW_RUN_TEST_CASE(GettersSetters, parcBuffer_ToHexString_NULLBuffer); + LONGBOW_RUN_TEST_CASE(GettersSetters, parcBuffer_Display); + LONGBOW_RUN_TEST_CASE(GettersSetters, parcBuffer_Display_NULL); +} + +LONGBOW_TEST_FIXTURE_SETUP(GettersSetters) +{ + PARCBuffer *buffer = parcBuffer_Allocate(100); + + longBowTestCase_SetClipBoardData(testCase, buffer); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(GettersSetters) +{ + PARCBuffer *buffer = longBowTestCase_GetClipBoardData(testCase); + parcBuffer_Release(&buffer); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(GettersSetters, parcPutGetUint8) +{ + PARCBuffer *buffer = longBowTestCase_GetClipBoardData(testCase); + + uint8_t expected = 0x12; + parcBuffer_PutUint8(buffer, expected); + parcBuffer_Flip(buffer); + uint8_t actual = parcBuffer_GetUint8(buffer); + + assertTrue(expected == actual, "Expected %d, actual %d", expected, actual); +} + +LONGBOW_TEST_CASE(GettersSetters, parcPutGetUint16) +{ + PARCBuffer *buffer = longBowTestCase_GetClipBoardData(testCase); + + uint16_t expected = 0x1234; + parcBuffer_PutUint16(buffer, expected); + parcBuffer_Flip(buffer); + uint16_t actual = parcBuffer_GetUint16(buffer); + + assertTrue(expected == actual, "Expected %d, actual %d", expected, actual); +} + +LONGBOW_TEST_CASE(GettersSetters, parcPutGetUint32) +{ + PARCBuffer *buffer = longBowTestCase_GetClipBoardData(testCase); + + uint32_t expected = 0x12345678; + parcBuffer_PutUint32(buffer, expected); + parcBuffer_Flip(buffer); + uint32_t actual = parcBuffer_GetUint32(buffer); + + assertTrue(expected == actual, "Expected %d, actual %d", expected, actual); +} + +LONGBOW_TEST_CASE(GettersSetters, parcPutGetUint64) +{ + PARCBuffer *buffer = longBowTestCase_GetClipBoardData(testCase); + + uint64_t expected = 0x1234567812345678; + parcBuffer_PutUint64(buffer, expected); + parcBuffer_Flip(buffer); + uint64_t actual = parcBuffer_GetUint64(buffer); + + assertTrue(expected == actual, "Expected %" PRIu64 ", actual %" PRIu64 "", expected, actual); +} + +LONGBOW_TEST_CASE(GettersSetters, parcBuffer_ToHexString) +{ + PARCBuffer *buffer = longBowTestCase_GetClipBoardData(testCase); + + uint64_t expected = 0x1234567812345678; + parcBuffer_PutUint64(buffer, expected); + parcBuffer_Flip(buffer); + char *hexString = parcBuffer_ToHexString(buffer); + + assertTrue(strcmp("1234567812345678", hexString) == 0, "Expected 1234567812345678, actual %s", hexString); + parcMemory_Deallocate((void **) &hexString); +} + +LONGBOW_TEST_CASE(GettersSetters, parcBuffer_ToHexString_NULLBuffer) +{ + char *hexString = parcBuffer_ToHexString(NULL); + + assertTrue(strcmp("null", hexString) == 0, "Expected null, actual %s", hexString); + parcMemory_Deallocate((void **) &hexString); +} + +LONGBOW_TEST_CASE(GettersSetters, parcBuffer_Display) +{ + PARCBuffer *buffer = longBowTestCase_GetClipBoardData(testCase); + + uint64_t expected = 0x1234567812345678; + parcBuffer_PutUint64(buffer, expected); + parcBuffer_Flip(buffer); + parcBuffer_Display(buffer, 0); +} + +LONGBOW_TEST_CASE(GettersSetters, parcBuffer_Display_NULL) +{ + parcBuffer_Display(NULL, 0); +} + +LONGBOW_TEST_FIXTURE(Errors) +{ + LONGBOW_RUN_TEST_CASE(Errors, parcBuffer_GetByte_Underflow); + LONGBOW_RUN_TEST_CASE(Errors, parcBuffer_Mark_mark_exceeds_position); +} + +typedef struct parc_buffer_longbow_clipboard { + PARCBuffer *buffer; +} parcBuffer_LongBowClipBoard; + +LONGBOW_TEST_FIXTURE_SETUP(Errors) +{ + parcBuffer_LongBowClipBoard *testData = calloc(1, sizeof(parcBuffer_LongBowClipBoard)); + testData->buffer = parcBuffer_Allocate(10); + + longBowTestCase_SetClipBoardData(testCase, testData); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Errors) +{ + parcBuffer_LongBowClipBoard *testData = longBowTestCase_GetClipBoardData(testCase); + parcBuffer_Release(&testData->buffer); + free(testData); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE_EXPECTS(Errors, parcBuffer_GetByte_Underflow, .event = &LongBowTrapOutOfBounds) +{ + parcBuffer_LongBowClipBoard *testData = longBowTestCase_GetClipBoardData(testCase); + PARCBuffer *buffer = testData->buffer; + + uint8_t expected[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + parcBuffer_PutArray(buffer, 1, expected); + parcBuffer_Flip(buffer); + + parcBuffer_GetUint8(buffer); + parcBuffer_GetUint8(buffer); // this will fail. +} + +LONGBOW_TEST_CASE_EXPECTS(Errors, parcBuffer_Mark_mark_exceeds_position, .event = &LongBowAssertEvent) +{ + parcBuffer_LongBowClipBoard *testData = longBowTestCase_GetClipBoardData(testCase); + PARCBuffer *buffer = testData->buffer; + + size_t expected = 2; + parcBuffer_SetPosition(buffer, expected); + parcBuffer_Mark(buffer); + parcBuffer_SetPosition(buffer, 0); + parcBuffer_Reset(buffer); +} + + +LONGBOW_TEST_FIXTURE(Static) +{ + LONGBOW_RUN_TEST_CASE(Static, _digittoint); +} + +LONGBOW_TEST_FIXTURE_SETUP(Static) +{ + _longBowGlobal_Global_outstanding = parcMemory_Outstanding(); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Static) +{ + LongBowStatus result = LONGBOW_STATUS_SUCCEEDED; + + size_t allocationsLeaked = parcMemory_Outstanding() - _longBowGlobal_Global_outstanding; + + if (allocationsLeaked) { + printf("%s leaks memory by %zd allocations\n", longBowTestCase_GetName(testCase), allocationsLeaked); + parcSafeMemory_ReportAllocation(STDERR_FILENO); + result = LONGBOW_STATUS_MEMORYLEAK; + } + return result; +} + +LONGBOW_TEST_CASE(Static, _digittoint) +{ + char *base10 = "0123456789"; + + for (size_t i = 0; i < strlen(base10); i++) { + int expected = (int) i; + int actual = _digittoint(base10[i]); + assertTrue(expected == actual, "Expected %d, actual %d", expected, actual); + } + + char *base16 = "0123456789abcdef"; + + for (size_t i = 0; i < strlen(base16); i++) { + int expected = (int) i; + int actual = _digittoint(base16[i]); + assertTrue(expected == actual, "Expected %d, actual %d", expected, actual); + } + + base16 = "0123456789ABCDEF"; + + for (size_t i = 0; i < strlen(base16); i++) { + int expected = (int) i; + int actual = _digittoint(base16[i]); + assertTrue(expected == actual, "Expected %d, actual %d", expected, actual); + } +} + +LONGBOW_TEST_FIXTURE_OPTIONS(Performance, .enabled = false) +{ + LONGBOW_RUN_TEST_CASE(Performance, parcBuffer_Create); +} + +LONGBOW_TEST_FIXTURE_SETUP(Performance) +{ + _longBowGlobal_Global_outstanding = parcMemory_Outstanding(); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Performance) +{ + LongBowStatus result = LONGBOW_STATUS_SUCCEEDED; + + size_t allocationsLeaked = parcMemory_Outstanding() - _longBowGlobal_Global_outstanding; + + if (allocationsLeaked) { + printf("%s leaks memory by %zd allocations\n", longBowTestCase_GetName(testCase), allocationsLeaked); + parcSafeMemory_ReportAllocation(STDERR_FILENO); + result = LONGBOW_STATUS_MEMORYLEAK; + } + return result; +} + +LONGBOW_TEST_CASE(Performance, parcBuffer_Create) +{ + for (size_t i = 0; i < 1000000; i++) { + PARCBuffer *buffer = parcBuffer_Allocate(1200); + parcBuffer_Release(&buffer); + } +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parcBuffer); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_BufferChunker.c b/libparc/parc/algol/test/test_parc_BufferChunker.c new file mode 100755 index 00000000..8f770968 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_BufferChunker.c @@ -0,0 +1,336 @@ +/* + * 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 the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../parc_BufferChunker.c" + +#include <stdio.h> + +#include <LongBow/unit-test.h> +#include <parc/algol/parc_SafeMemory.h> + +LONGBOW_TEST_RUNNER(parc_BufferChunker) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_BufferChunker) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_BufferChunker) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parc_Chunker_CreateFromBuffer); + LONGBOW_RUN_TEST_CASE(Global, parc_Chunker_ForwardIterator_Buffer); + LONGBOW_RUN_TEST_CASE(Global, parc_Chunker_ForwardIterator_BufferPartial); + LONGBOW_RUN_TEST_CASE(Global, parc_Chunker_ForwardIterator_BufferSmall); + LONGBOW_RUN_TEST_CASE(Global, parc_Chunker_ReverseIterator_Buffer); + LONGBOW_RUN_TEST_CASE(Global, parc_Chunker_ReverseIterator_BufferPartial); + LONGBOW_RUN_TEST_CASE(Global, parc_Chunker_ReverseIterator_BufferSmall); + LONGBOW_RUN_TEST_CASE(Global, parc_Chunker_GetChunkSize); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parc_Chunker_CreateFromBuffer) +{ + PARCBuffer *buffer = parcBuffer_Allocate(1024); + + PARCBufferChunker *chunker = parcBufferChunker_Create(buffer, 32); // each chunk is 32 bytes + assertNotNull(chunker, "Expected non-NULL Chunker"); + PARCBufferChunker *copy = parcBufferChunker_Acquire(chunker); + assertNotNull(chunker, "Expected non-NULL Chunker"); + assertNotNull(copy, "Expected non-NULL copy of Chunker"); + parcBufferChunker_Release(©); + + parcBuffer_Release(&buffer); + parcBufferChunker_Release(&chunker); +} + +LONGBOW_TEST_CASE(Global, parc_Chunker_ForwardIterator_Buffer) +{ + PARCBuffer *buffer = parcBuffer_Allocate(1024); + + for (size_t i = 0; i < 32; i++) { + for (size_t j = 0; j < 32; j++) { + parcBuffer_PutUint8(buffer, i); + } + } + parcBuffer_Flip(buffer); + + PARCBufferChunker *chunker = parcBufferChunker_Create(buffer, 32); // each chunk is 32 bytes + assertNotNull(chunker, "Expected non-NULL Chunker"); + + PARCIterator *itr = parcBufferChunker_ForwardIterator(chunker); + size_t count = 0; + while (parcIterator_HasNext(itr)) { + PARCBuffer *payload = (PARCBuffer *) parcIterator_Next(itr); + + uint8_t *contents = parcBuffer_Overlay(payload, 0); + for (size_t i = 0; i < 32; i++) { + assertTrue(contents[i] == count, "Expected %zu at index %zu, got %d", count, i, contents[i]); + } + count++; + + parcBuffer_Release(&payload); + } + assertTrue(count == 32, "Expected to iterate over 32 content objects from the chunker, but for %zu", count); + parcIterator_Release(&itr); + + parcBufferChunker_Release(&chunker); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parc_Chunker_ForwardIterator_BufferPartial) +{ + // Allocate something that's not divisible by the chunk size + PARCBuffer *buffer = parcBuffer_Allocate(1030); + + for (size_t i = 0; i < 32; i++) { + for (size_t j = 0; j < 32; j++) { + parcBuffer_PutUint8(buffer, i); + } + } + + // Special 0xFF to mark the end... + for (int i = 0; i < 6; i++) { + parcBuffer_PutUint8(buffer, 0xFF); + } + + parcBuffer_Flip(buffer); + + PARCBufferChunker *chunker = parcBufferChunker_Create(buffer, 32); // each chunk is 32 bytes + assertNotNull(chunker, "Expected non-NULL Chunker"); + + PARCIterator *itr = parcBufferChunker_ForwardIterator(chunker); + size_t count = 0; + while (parcIterator_HasNext(itr)) { + PARCBuffer *payload = (PARCBuffer *) parcIterator_Next(itr); + + uint8_t *contents = parcBuffer_Overlay(payload, 0); + if (count < 32) { + for (size_t i = 0; i < 32; i++) { + assertTrue(contents[i] == count, "Expected %zu at index %zu, got %d", count, i, contents[i]); + } + } else { + for (size_t i = 0; i < 6; i++) { + assertTrue(contents[i] == 0xFF, "Expected %zu at index %zu, got %d", (size_t) 0xFF, i, contents[i]); + } + } + count++; + + parcBuffer_Release(&payload); + } + assertTrue(count == 33, "Expected to iterate over 33 content objects from the chunker, but for %zu", count); + parcIterator_Release(&itr); + + parcBufferChunker_Release(&chunker); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parc_Chunker_ForwardIterator_BufferSmall) +{ + // Allocate something that's not divisible by the chunk size + PARCBuffer *buffer = parcBuffer_Allocate(16); + + // Special 0xFF to mark the end... + for (int i = 0; i < 16; i++) { + parcBuffer_PutUint8(buffer, 0xFF); + } + + parcBuffer_Flip(buffer); + + PARCBufferChunker *chunker = parcBufferChunker_Create(buffer, 32); // each chunk is 32 bytes + assertNotNull(chunker, "Expected non-NULL Chunker"); + + PARCIterator *itr = parcBufferChunker_ForwardIterator(chunker); + size_t count = 0; + while (parcIterator_HasNext(itr)) { + PARCBuffer *payload = (PARCBuffer *) parcIterator_Next(itr); + + uint8_t *contents = parcBuffer_Overlay(payload, 0); + for (size_t i = 0; i < 16; i++) { + assertTrue(contents[i] == 0xFF, "Expected %zu at index %zu, got %d", (size_t) 0xFF, i, contents[i]); + } + count++; + + parcBuffer_Release(&payload); + } + assertTrue(count == 1, "Expected to iterate over 1 content objects from the chunker, but for %zu", count); + parcIterator_Release(&itr); + + parcBufferChunker_Release(&chunker); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parc_Chunker_ReverseIterator_Buffer) +{ + PARCBuffer *buffer = parcBuffer_Allocate(1024); + + for (size_t i = 0; i < 32; i++) { + for (size_t j = 0; j < 32; j++) { + parcBuffer_PutUint8(buffer, i); + } + } + parcBuffer_Flip(buffer); + + PARCBufferChunker *chunker = parcBufferChunker_Create(buffer, 32); // each chunk is 32 bytes + assertNotNull(chunker, "Expected non-NULL Chunker"); + + PARCIterator *itr = parcBufferChunker_ReverseIterator(chunker); + size_t count = 0; + while (parcIterator_HasNext(itr)) { + PARCBuffer *payload = (PARCBuffer *) parcIterator_Next(itr); + + uint8_t *contents = parcBuffer_Overlay(payload, 0); + for (size_t i = 0; i < 32; i++) { + assertTrue(contents[i] == (31 - count), "Expected %zu at index %zu, got %d", (31 - count), i, contents[i]); + } + count++; + + parcBuffer_Release(&payload); + } + assertTrue(count == 32, "Expected to iterate over 32 content objects from the chunker, but for %zu", count); + parcIterator_Release(&itr); + + parcBufferChunker_Release(&chunker); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parc_Chunker_ReverseIterator_BufferPartial) +{ + PARCBuffer *buffer = parcBuffer_Allocate(1030); + + // Special 0xFF to mark the start + for (int i = 0; i < 6; i++) { + parcBuffer_PutUint8(buffer, 0xFF); + } + + for (size_t i = 0; i < 32; i++) { + for (size_t j = 0; j < 32; j++) { + parcBuffer_PutUint8(buffer, i); + } + } + parcBuffer_Flip(buffer); + + PARCBufferChunker *chunker = parcBufferChunker_Create(buffer, 32); // each chunk is 32 bytes + assertNotNull(chunker, "Expected non-NULL Chunker"); + + PARCIterator *itr = parcBufferChunker_ReverseIterator(chunker); + size_t count = 0; + while (parcIterator_HasNext(itr)) { + PARCBuffer *payload = (PARCBuffer *) parcIterator_Next(itr); + + uint8_t *contents = parcBuffer_Overlay(payload, 0); + if (count < 32) { + for (size_t i = 0; i < 32; i++) { + assertTrue(contents[i] == (31 - count), "Expected %zu at index %zu, got %d", count, i, contents[i]); + } + } else { + for (size_t i = 0; i < 6; i++) { + assertTrue(contents[i] == 0xFF, "Expected %zu at index %zu, got %d", (size_t) 0xFF, i, contents[i]); + } + } + count++; + + parcBuffer_Release(&payload); + } + assertTrue(count == 33, "Expected to iterate over 33 content objects from the chunker, but for %zu", count); + parcIterator_Release(&itr); + + parcBufferChunker_Release(&chunker); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parc_Chunker_ReverseIterator_BufferSmall) +{ + PARCBuffer *buffer = parcBuffer_Allocate(16); + + // Special 0xFF to mark the start + for (int i = 0; i < 16; i++) { + parcBuffer_PutUint8(buffer, 0xFF); + } + parcBuffer_Flip(buffer); + + PARCBufferChunker *chunker = parcBufferChunker_Create(buffer, 32); // each chunk is 32 bytes + assertNotNull(chunker, "Expected non-NULL Chunker"); + + PARCIterator *itr = parcBufferChunker_ReverseIterator(chunker); + size_t count = 0; + while (parcIterator_HasNext(itr)) { + PARCBuffer *payload = (PARCBuffer *) parcIterator_Next(itr); + + uint8_t *contents = parcBuffer_Overlay(payload, 0); + for (size_t i = 0; i < 16; i++) { + assertTrue(contents[i] == 0xFF, "Expected %zu at index %zu, got %d", (size_t) 0xFF, i, contents[i]); + } + count++; + + parcBuffer_Release(&payload); + } + assertTrue(count == 1, "Expected to iterate over 1 content objects from the chunker, but for %zu", count); + parcIterator_Release(&itr); + + parcBufferChunker_Release(&chunker); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parc_Chunker_GetChunkSize) +{ + size_t expectedChunkSize = 32; + PARCBuffer *buffer = parcBuffer_Allocate(16); + PARCBufferChunker *chunker = parcBufferChunker_Create(buffer, expectedChunkSize); // each chunk is 32 bytes + size_t actualChunkSize = parcBufferChunker_GetChunkSize(chunker); + + assertTrue(actualChunkSize == expectedChunkSize, "Expected chunk size of %zu, got %zu", expectedChunkSize, actualChunkSize); + + parcBuffer_Release(&buffer); + parcBufferChunker_Release(&chunker); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_BufferChunker); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_BufferComposer.c b/libparc/parc/algol/test/test_parc_BufferComposer.c new file mode 100644 index 00000000..fad9857c --- /dev/null +++ b/libparc/parc/algol/test/test_parc_BufferComposer.c @@ -0,0 +1,448 @@ +/* + * 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 the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../parc_BufferComposer.c" + +#include <inttypes.h> +#include <LongBow/unit-test.h> +#include <LongBow/debugging.h> + +#include <parc/algol/parc_SafeMemory.h> +#include <parc/testing/parc_ObjectTesting.h> + +typedef struct { + uint32_t setupAllocations; + PARCBufferComposer *composer; +} TestData; + +static TestData* +commonSetup() +{ + uint32_t outstanding = parcSafeMemory_Outstanding(); + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear of %zu bytes returned NULL.", sizeof(TestData)); + data->setupAllocations = outstanding; + data->composer = parcBufferComposer_Create(); + return data; +} + +static uint32_t +commonTearDown(TestData *data) +{ + uint32_t setupAllocations = data->setupAllocations; + + // NOTE: The `parcBufferComposer_AssertValid_IncrementSize` invalidates this object, so we must + // restore it to a good state in order for all memory to be released correctly. + (data->composer)->incrementHeuristic = sizeof(void *); + + parcBufferComposer_Release(&(data->composer)); + parcMemory_Deallocate((void **) &data); + return parcSafeMemory_ReportAllocation(STDOUT_FILENO) - setupAllocations; +} + +LONGBOW_TEST_RUNNER(parc_BufferComposer) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_BufferComposer) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_BufferComposer) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcBufferComposer_Acquire); + LONGBOW_RUN_TEST_CASE(Global, parcBufferComposer_Allocate); + LONGBOW_RUN_TEST_CASE(Global, parcBufferComposer_Allocate_SizeMax); + LONGBOW_RUN_TEST_CASE(Global, parcBufferComposer_AssertValid); + LONGBOW_RUN_TEST_CASE(Global, parcBufferComposer_AssertValid_NULL); + LONGBOW_RUN_TEST_CASE(Global, parcBufferComposer_AssertValid_IncrementSize); + LONGBOW_RUN_TEST_CASE(Global, parcBufferComposer_Create); + LONGBOW_RUN_TEST_CASE(Global, parcBufferComposer_Equals); + LONGBOW_RUN_TEST_CASE(Global, parcBufferComposer_PutArray); + LONGBOW_RUN_TEST_CASE(Global, parcBufferComposer_PutBuffer); + LONGBOW_RUN_TEST_CASE(Global, parcBufferComposer_PutUint16); + LONGBOW_RUN_TEST_CASE(Global, parcBufferComposer_PutUint32); + LONGBOW_RUN_TEST_CASE(Global, parcBufferComposer_PutUint64); + LONGBOW_RUN_TEST_CASE(Global, parcBufferComposer_PutUint8); + LONGBOW_RUN_TEST_CASE(Global, parcBufferComposer_PutString); + LONGBOW_RUN_TEST_CASE(Global, parcBufferComposer_PutStrings); + LONGBOW_RUN_TEST_CASE(Global, parcBufferComposer_Format); + LONGBOW_RUN_TEST_CASE(Global, parcBufferComposer_PutChar); + LONGBOW_RUN_TEST_CASE(Global, parcBufferComposer_GetBuffer); + LONGBOW_RUN_TEST_CASE(Global, parcBufferComposer_CreateBuffer); + LONGBOW_RUN_TEST_CASE(Global, parcBufferComposer_ProduceBuffer); + LONGBOW_RUN_TEST_CASE(Global, parcBufferComposer_PutString_Extend); + LONGBOW_RUN_TEST_CASE(Global, parcBufferComposer_ToString); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + TestData *data = commonSetup(); + longBowTestCase_SetClipBoardData(testCase, data); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + uint32_t outstandingAllocations = commonTearDown(data); + + if (outstandingAllocations != 0) { + printf("%s leaks %d memory allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcBufferComposer_Acquire) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + + PARCBufferComposer *handle = parcBufferComposer_Acquire(composer); + + assertNotNull(handle, "Acquired PARCBufferComposer handle should be non-NULL."); + assertTrue(parcBufferComposer_Equals(composer, handle), "PARCBufferComposer instances should be equal"); + + parcBufferComposer_Release(&handle); + parcBufferComposer_Release(&composer); +} + +LONGBOW_TEST_CASE(Global, parcBufferComposer_Allocate) +{ + size_t size = 10; + PARCBufferComposer *composer = parcBufferComposer_Allocate(size); + PARCBuffer *buffer = parcBufferComposer_GetBuffer(composer); + + assertNotNull(composer, "PARCBufferComposer instance should be non-NULL."); + assertTrue(parcBuffer_Limit(buffer) == size, + "PARCBufferComposer instance's internal PARCBuffer limit must be %zu: %zu", size, parcBuffer_Limit(buffer)); + + parcBufferComposer_Release(&composer); +} + +LONGBOW_TEST_CASE(Global, parcBufferComposer_Allocate_SizeMax) +{ + size_t size = SIZE_MAX; + PARCBufferComposer *composer = parcBufferComposer_Allocate(size); + + assertNull(composer, "PARCBufferComposer instance should be NULL."); +} + +LONGBOW_TEST_CASE(Global, parcBufferComposer_AssertValid) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + + assertNotNull(composer, "PARCBufferComposer instance should be non-NULL."); + parcBufferComposer_AssertValid(composer); + + parcBufferComposer_Release(&composer); +} + +LONGBOW_TEST_CASE_EXPECTS(Global, parcBufferComposer_AssertValid_NULL, .event = &LongBowTrapIllegalValue) +{ + parcBufferComposer_AssertValid(NULL); +} + +LONGBOW_TEST_CASE_EXPECTS(Global, parcBufferComposer_AssertValid_IncrementSize, .event = &LongBowTrapIllegalValue) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + (data->composer)->incrementHeuristic = 0; // must be >= sizeof(void *) + parcBufferComposer_AssertValid(data->composer); +} + +LONGBOW_TEST_CASE(Global, parcBufferComposer_Create) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + + assertNotNull(composer, "PARCBufferComposer instance should be non-NULL."); + + parcBufferComposer_Release(&composer); +} + +LONGBOW_TEST_CASE(Global, parcBufferComposer_Equals) +{ + PARCBufferComposer *x = parcBufferComposer_Create(); + PARCBufferComposer *y = parcBufferComposer_Create(); + PARCBufferComposer *z = parcBufferComposer_Create(); + PARCBufferComposer *u = parcBufferComposer_Allocate(10); + + parcObjectTesting_AssertEqualsFunction(parcBufferComposer_Equals, x, y, z, u); + + u->incrementHeuristic = 0; + assertFalse(parcBufferComposer_Equals(x, u), "PARCBufferComposer instances should be unequal due to size increments."); + assertFalse(parcBufferComposer_Equals(x, NULL), "Equals should return false since the second parameter is NULL."); + u->incrementHeuristic = sizeof(void *); + + parcBufferComposer_Release(&x); + parcBufferComposer_Release(&y); + parcBufferComposer_Release(&z); + parcBufferComposer_Release(&u); +} + +LONGBOW_TEST_CASE(Global, parcBufferComposer_ToString) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + parcBufferComposer_Format(composer, "hello %s", "world"); + + char *string = parcBufferComposer_ToString(composer); + + assertNotNull(string, "Expected non-NULL result from parcBufferComposer_ToString"); + + parcMemory_Deallocate((void **) &string); + + parcBufferComposer_Release(&composer); +} + +LONGBOW_TEST_CASE(Global, parcBufferComposer_GetBuffer) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + PARCBuffer *buffer = parcBufferComposer_GetBuffer(composer); + + assertNotNull(composer, "PARCBufferComposer instance should be non-NULL."); + assertNotNull(buffer, "PARCBufferComposer instance's internal PARCBuffer should not be NULL"); + + parcBufferComposer_Release(&composer); +} + +LONGBOW_TEST_CASE(Global, parcBufferComposer_PutArray) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + + uint8_t string[6] = { 'h', 'e', 'l', 'l', 'o', '\0' }; + parcBufferComposer_PutArray(composer, string, 6); + + PARCBuffer *buffer = parcBufferComposer_ProduceBuffer(composer); + + char expected[5] = "hello"; + char *actual = parcBuffer_ToString(buffer); + assertTrue(strncmp(expected, actual, 5) == 0, "Expected strings to match. Got %s, expected %s", actual, expected); + + parcMemory_Deallocate((void **) &actual); + parcBuffer_Release(&buffer); + parcBufferComposer_Release(&composer); +} + +LONGBOW_TEST_CASE(Global, parcBufferComposer_PutBuffer) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + + PARCBuffer *insertee = parcBuffer_WrapCString("hello world"); + parcBufferComposer_PutBuffer(composer, insertee); + parcBuffer_Release(&insertee); + + PARCBuffer *buffer = parcBufferComposer_ProduceBuffer(composer); + + char expected[11] = "hello world"; + char *actual = parcBuffer_ToString(buffer); + assertTrue(strncmp(expected, actual, 11) == 0, "Expected strings to match. Got %s, expected %s", actual, expected); + + parcMemory_Deallocate((void **) &actual); + parcBuffer_Release(&buffer); + parcBufferComposer_Release(&composer); +} + +LONGBOW_TEST_CASE(Global, parcBufferComposer_PutUint16) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + + uint16_t val = 0x0101; + parcBufferComposer_PutUint16(composer, val); + + PARCBuffer *buffer = parcBufferComposer_ProduceBuffer(composer); + assertTrue(parcBuffer_GetUint16(buffer) == val, "Expected inserted uint16_t to be equal to %x, got %x", val, parcBuffer_GetUint16(buffer)); + + parcBuffer_Release(&buffer); + parcBufferComposer_Release(&composer); +} + +LONGBOW_TEST_CASE(Global, parcBufferComposer_PutUint32) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + + uint32_t val = 0x0101FFFF; + parcBufferComposer_PutUint32(composer, val); + + PARCBuffer *buffer = parcBufferComposer_ProduceBuffer(composer); + assertTrue(parcBuffer_GetUint32(buffer) == val, "Expected inserted uint32_t to be equal to %x, got %x", val, parcBuffer_GetUint32(buffer)); + + parcBuffer_Release(&buffer); + parcBufferComposer_Release(&composer); +} + +LONGBOW_TEST_CASE(Global, parcBufferComposer_PutUint64) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + + uint64_t val = 0x0101FFFFABCD0123; + parcBufferComposer_PutUint64(composer, val); + + PARCBuffer *buffer = parcBufferComposer_ProduceBuffer(composer); + assertTrue(parcBuffer_GetUint64(buffer) == val, + "Expected inserted uint64_t to be equal to %" PRIu64 ", got %" PRIu64 "", val, parcBuffer_GetUint64(buffer)); + + parcBuffer_Release(&buffer); + parcBufferComposer_Release(&composer); +} + +LONGBOW_TEST_CASE(Global, parcBufferComposer_PutUint8) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + + uint8_t byte = 0x01; + parcBufferComposer_PutUint8(composer, byte); + + PARCBuffer *buffer = parcBufferComposer_ProduceBuffer(composer); + assertTrue(parcBuffer_GetUint8(buffer) == byte, "Expected inserted byte to be equal to %x, got %x", byte, parcBuffer_GetUint8(buffer)); + + parcBuffer_Release(&buffer); + parcBufferComposer_Release(&composer); +} + +LONGBOW_TEST_CASE(Global, parcBufferComposer_PutString) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + + char string[14] = "Hello, World!"; + parcBufferComposer_PutString(composer, string); + + PARCBuffer *buffer = parcBufferComposer_ProduceBuffer(composer); + char *actual = parcBuffer_ToString(buffer); + assertTrue(strncmp(actual, string, strlen(string)) == 0, + "Expected inserted string to be equal to %s, got %s", string, actual); + + parcMemory_Deallocate((void **) &actual); + parcBuffer_Release(&buffer); + parcBufferComposer_Release(&composer); +} + +LONGBOW_TEST_CASE(Global, parcBufferComposer_PutStrings) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + + char string[14] = "Hello, World!"; + parcBufferComposer_PutStrings(composer, "Hello", ", ", "World!", NULL); + + PARCBuffer *buffer = parcBufferComposer_ProduceBuffer(composer); + char *actual = parcBuffer_ToString(buffer); + assertTrue(strncmp(actual, string, strlen(string)) == 0, "Expected inserted string to be equal to %s, got %s", string, actual); + + parcMemory_Deallocate((void **) &actual); + parcBuffer_Release(&buffer); + parcBufferComposer_Release(&composer); +} + +LONGBOW_TEST_CASE(Global, parcBufferComposer_Format) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + parcBufferComposer_Format(composer, "hello %s", "world"); + + PARCBuffer *buffer = parcBufferComposer_ProduceBuffer(composer); + + char expected[11] = "hello world"; + char *actual = parcBuffer_ToString(buffer); + assertTrue(strncmp(expected, actual, 11) == 0, "Expected strings to match. Got %s, expected %s", actual, expected); + + parcMemory_Deallocate((void **) &actual); + parcBuffer_Release(&buffer); + parcBufferComposer_Release(&composer); +} + +LONGBOW_TEST_CASE(Global, parcBufferComposer_PutChar) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + + char byte = 'a'; + parcBufferComposer_PutChar(composer, byte); + + PARCBuffer *buffer = parcBufferComposer_ProduceBuffer(composer); + assertTrue(parcBuffer_GetUint8(buffer) == byte, "Expected inserted char to be equal to %c, got %c", byte, parcBuffer_GetUint8(buffer)); + + parcBuffer_Release(&buffer); + parcBufferComposer_Release(&composer); +} + +LONGBOW_TEST_CASE(Global, parcBufferComposer_CreateBuffer) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + parcBufferComposer_PutString(composer, "hello world"); + + PARCBuffer *buffer = parcBufferComposer_CreateBuffer(composer); + parcBuffer_Flip(buffer); // flip the buffer since it was returned in write mode + + char expected[11] = "hello world"; + char *actual = parcBuffer_ToString(buffer); + assertTrue(strncmp(expected, actual, 11) == 0, "Expected strings to match. Got %s, expected %s", actual, expected); + + parcMemory_Deallocate((void **) &actual); + parcBuffer_Release(&buffer); + parcBufferComposer_Release(&composer); +} + +LONGBOW_TEST_CASE(Global, parcBufferComposer_ProduceBuffer) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + parcBufferComposer_PutString(composer, "hello world"); + + PARCBuffer *buffer = parcBufferComposer_ProduceBuffer(composer); + + char expected[11] = "hello world"; + char *actual = parcBuffer_ToString(buffer); + assertTrue(strncmp(expected, actual, 11) == 0, "Expected strings to match. Got %s, expected %s", actual, expected); + + parcMemory_Deallocate((void **) &actual); + parcBuffer_Release(&buffer); + parcBufferComposer_Release(&composer); +} + +LONGBOW_TEST_CASE(Global, parcBufferComposer_PutString_Extend) +{ + PARCBufferComposer *composer = parcBufferComposer_Allocate(4); + parcBufferComposer_PutString(composer, "hello world"); + + PARCBuffer *buffer = parcBufferComposer_ProduceBuffer(composer); + + char expected[11] = "hello world"; + char *actual = parcBuffer_ToString(buffer); + assertTrue(strncmp(expected, actual, 11) == 0, "Expected strings to match. Got %s, expected %s", actual, expected); + + parcMemory_Deallocate((void **) &actual); + parcBuffer_Release(&buffer); + parcBufferComposer_Release(&composer); +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_BufferComposer); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_ByteArray.c b/libparc/parc/algol/test/test_parc_ByteArray.c new file mode 100755 index 00000000..971d79dd --- /dev/null +++ b/libparc/parc/algol/test/test_parc_ByteArray.c @@ -0,0 +1,499 @@ +/* + * 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. + */ + +/** + * @file test_parc_ByteArray.c + * + */ +#include "../parc_ByteArray.c" +#include <stdio.h> + +#include <LongBow/unit-test.h> +#include <LongBow/debugging.h> +#include <stdio.h> + +#include <parc/algol/parc_SafeMemory.h> +#include <parc/testing/parc_ObjectTesting.h> + +LONGBOW_TEST_RUNNER(PARCByteArray) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Errors); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(PARCByteArray) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(PARCByteArray) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcByteArray_Acquire); + LONGBOW_RUN_TEST_CASE(Global, parcByteArray_Acquire_destroyoriginal); + LONGBOW_RUN_TEST_CASE(Global, parcByteArray_Allocate); + LONGBOW_RUN_TEST_CASE(Global, parcByteArray_Allocate_ZeroLength); + + LONGBOW_RUN_TEST_CASE(Global, parcByteArray_Wrap_NULL); + LONGBOW_RUN_TEST_CASE(Global, parcByteArray_Wrap_ZeroLength); + + LONGBOW_RUN_TEST_CASE(Global, parcByteArray_Wrap); + LONGBOW_RUN_TEST_CASE(Global, parcByteArray_Array); + LONGBOW_RUN_TEST_CASE(Global, parcByteArray_AddressOfIndex); + LONGBOW_RUN_TEST_CASE(Global, parcByteArray_Capacity); + LONGBOW_RUN_TEST_CASE(Global, parcByteArray_Copy_Allocated); + LONGBOW_RUN_TEST_CASE(Global, parcByteArray_Copy_Wrapped); + LONGBOW_RUN_TEST_CASE(Global, parcByteArray_Compare); + LONGBOW_RUN_TEST_CASE(Global, parcByteArray_PutBytes); + LONGBOW_RUN_TEST_CASE(Global, parcByteArray_CopyOut); + LONGBOW_RUN_TEST_CASE(Global, parcByteArray_CopyInByteArray); + LONGBOW_RUN_TEST_CASE(Global, parcByteArray_Get); + LONGBOW_RUN_TEST_CASE(Global, parcByteArray_Put); + LONGBOW_RUN_TEST_CASE(Global, parcByteArray_Equals); + LONGBOW_RUN_TEST_CASE(Global, parcByteArray_HashCode); + LONGBOW_RUN_TEST_CASE(Global, parcByteArray_Release); + LONGBOW_RUN_TEST_CASE(Global, parcByteArray_Display); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcByteArray_Allocate) +{ + PARCByteArray *actual = parcByteArray_Allocate(10); + + parcByteArray_Release(&actual); +} + +LONGBOW_TEST_CASE(Global, parcByteArray_Allocate_ZeroLength) +{ + PARCByteArray *actual = parcByteArray_Allocate(0); + assertNotNull(actual, "parcByteArray_Allocate(0) must not return NULL."); + assertTrue(parcByteArray_Capacity(actual) == 0, "Expected capacity to be 0"); + + parcByteArray_Release(&actual); +} + +LONGBOW_TEST_CASE(Global, parcByteArray_Wrap) +{ + uint8_t buffer[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCByteArray *actual = parcByteArray_Wrap(10, buffer); + + parcByteArray_Release(&actual); +} + +LONGBOW_TEST_CASE(Global, parcByteArray_Wrap_NULL) +{ + PARCByteArray *actual = parcByteArray_Wrap(10, NULL); + + assertNull(actual, "Expected NULL return value from parcByteArray_Wrap()"); +} + +LONGBOW_TEST_CASE(Global, parcByteArray_Wrap_ZeroLength) +{ + PARCByteArray *actual = parcByteArray_Wrap(0, (uint8_t[1]) { 0 }); + + assertNotNull(actual, "Expected non-NULL return value from parcByteArray_Wrap()"); + assertTrue(parcByteArray_Capacity(actual) == 0, "Expected capacity to be zero."); + parcByteArray_Release(&actual); +} + +LONGBOW_TEST_CASE(Global, parcByteArray_Array) +{ + uint8_t buffer[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCByteArray *actual = parcByteArray_Wrap(10, buffer); + assertTrue(buffer == parcByteArray_Array(actual), "Expected the array to be the wrapped array."); + + parcByteArray_Release(&actual); +} + +LONGBOW_TEST_CASE(Global, parcByteArray_AddressOfIndex) +{ + uint8_t buffer[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCByteArray *actual = parcByteArray_Wrap(10, buffer); + uint8_t *address = parcByteArray_AddressOfIndex(actual, 3); + + assertTrue(buffer[3] == *address, + "Expected %d, actual %d", buffer[3], *address); + + parcByteArray_Release(&actual); +} + + +LONGBOW_TEST_CASE(Global, parcByteArray_Release) +{ + PARCByteArray *actual = parcByteArray_Allocate(10); + + parcByteArray_Release(&actual); + assertNull(actual, "Expected the pointer to be NULL after parcByteArray_Release"); +} + +LONGBOW_TEST_CASE(Global, parcByteArray_Copy_Allocated) +{ + uint8_t buffer[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCByteArray *original = parcByteArray_Allocate(sizeof(buffer)); + parcByteArray_PutBytes(original, 0, sizeof(buffer), buffer); + + PARCByteArray *clone = parcByteArray_Copy(original); + + assertTrue(original != clone, "Expected clone to be a different instance that original."); + + assertTrue(parcByteArray_Equals(original, clone), "Expected the clone to be equal to the original."); + + parcByteArray_Release(&original); + parcByteArray_Release(&clone); +} + +LONGBOW_TEST_CASE(Global, parcByteArray_Copy_Wrapped) +{ + uint8_t buffer[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCByteArray *original = parcByteArray_Wrap(sizeof(buffer), buffer); + + PARCByteArray *clone = parcByteArray_Copy(original); + + assertTrue(original != clone, "Expected clone to be a different instance that original."); + + assertTrue(parcByteArray_Equals(original, clone), "Expected the clone to be equal to the original."); + + parcByteArray_Release(&original); + parcByteArray_Release(&clone); +} + +LONGBOW_TEST_CASE(Global, parcByteArray_Compare) +{ + uint8_t buffer[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCByteArray *x = parcByteArray_Wrap(sizeof(buffer), buffer); + + PARCByteArray **equivalents = (PARCByteArray *[]) { + parcByteArray_Wrap(sizeof(buffer), buffer), + NULL + }; + PARCByteArray **lessers = (PARCByteArray *[]) { + parcByteArray_Wrap(sizeof(buffer) - 1, buffer), + parcByteArray_Wrap(sizeof(buffer) - 1, (uint8_t[]) { 0, 1, 2, 3, 4, 5, 6, 7, 8, 8 }), + NULL + }; + PARCByteArray **greaters = (PARCByteArray *[]) { + parcByteArray_Wrap(11, (uint8_t[]) { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }), + parcByteArray_Wrap(10, (uint8_t[]) { 0, 1, 2, 3, 4, 5, 6, 7, 8, 10 }), + NULL + }; + /* + * (a - b) + */ + parcObjectTesting_AssertCompareTo(parcByteArray_Compare, x, equivalents, lessers, greaters); + + parcByteArray_Release(&x); + + for (int i = 0; equivalents[i] != NULL; i++) { + parcByteArray_Release(&equivalents[i]); + } + for (int i = 0; lessers[i] != NULL; i++) { + parcByteArray_Release(&lessers[i]); + } + for (int i = 0; greaters[i] != NULL; i++) { + parcByteArray_Release(&greaters[i]); + } +} + +LONGBOW_TEST_CASE(Global, parcByteArray_Equals) +{ + uint8_t buffer[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCByteArray *x = parcByteArray_Wrap(10, buffer); + PARCByteArray *y = parcByteArray_Wrap(10, buffer); + PARCByteArray *z = parcByteArray_Wrap(10, buffer); + PARCByteArray *u1 = parcByteArray_Wrap(5, buffer); + PARCByteArray *u2 = parcByteArray_Allocate(5); + + parcObjectTesting_AssertEqualsFunction(parcByteArray_Equals, x, y, z, u1, u2, NULL); + + parcByteArray_Release(&x); + parcByteArray_Release(&y); + parcByteArray_Release(&z); + parcByteArray_Release(&u1); + parcByteArray_Release(&u2); +} + +LONGBOW_TEST_CASE(Global, parcByteArray_Capacity) +{ + size_t expected = 10; + + PARCByteArray *actual = parcByteArray_Allocate(expected); + assertTrue(expected == parcByteArray_Capacity(actual), "Expected %zd, actual %zd", expected, parcByteArray_Capacity(actual)); + + parcByteArray_Release(&actual); +} + +LONGBOW_TEST_CASE(Global, parcByteArray_CopyOut) +{ + uint8_t expected[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + uint8_t actual[10]; + + PARCByteArray *original = parcByteArray_Wrap(10, expected); + parcByteArray_GetBytes(original, 0, sizeof(actual), actual); + + assertTrue(memcmp(expected, actual, sizeof(actual)) == 0, + "Expected parcByteArray_CopyOut to copy the orginal data"); + + parcByteArray_Release(&original); +} + +LONGBOW_TEST_CASE(Global, parcByteArray_PutBytes) +{ + uint8_t expected[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + uint8_t actual[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + PARCByteArray *original = parcByteArray_Wrap(10, expected); + parcByteArray_PutBytes(original, 0, 10, actual); + + assertTrue(memcmp(expected, actual, 10) == 0, + "Expected parcByteArray_CopyOut to copy the orginal data"); + + parcByteArray_Release(&original); +} + +LONGBOW_TEST_CASE(Global, parcByteArray_CopyInByteArray) +{ + uint8_t array1[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + uint8_t array2[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + uint8_t expected[10] = { 0, 1, 2, 0, 0, 0, 6, 7, 8, 9 }; + + PARCByteArray *a1 = parcByteArray_Wrap(10, array1); + PARCByteArray *a2 = parcByteArray_Wrap(10, array2); + parcByteArray_ArrayCopy(a1, 3, a2, 0, 3); + + assertTrue(memcmp(expected, parcByteArray_Array(a1), 10) == 0, + "Expected parcByteArray_CopyOut to copy the orginal data"); + + parcByteArray_Release(&a1); + parcByteArray_Release(&a2); +} + +LONGBOW_TEST_CASE(Global, parcByteArray_Get) +{ + uint8_t buffer[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCByteArray *original = parcByteArray_Wrap(10, buffer); + + for (uint8_t index = 0; index < 10; index++) { + uint8_t actual = parcByteArray_GetByte(original, index); + assertTrue(index == actual, "Expected %d, actual %d", index, actual); + } + parcByteArray_Release(&original); +} + +LONGBOW_TEST_CASE(Global, parcByteArray_Put) +{ + uint8_t buffer[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + PARCByteArray *original = parcByteArray_Wrap(10, buffer); + + for (uint8_t index = 0; index < 10; index++) { + parcByteArray_PutByte(original, index, index); + } + + for (uint8_t index = 0; index < 10; index++) { + uint8_t actual = parcByteArray_GetByte(original, index); + assertTrue(index == actual, "Expected %d, actual %d", index, actual); + } + + parcByteArray_Release(&original); +} + +LONGBOW_TEST_CASE(Global, parcByteArray_Acquire) +{ + uint8_t buffer[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCByteArray *actual = parcByteArray_Wrap(10, buffer); + PARCByteArray *reference = parcByteArray_Acquire(actual); + + assertTrue(reference == actual, "Expected the new reference to be equal to the original."); + + PARCByteArray *new1 = parcByteArray_Acquire(actual); + assertTrue(new1 == actual, "Expected new to be the same as actual"); + + PARCByteArray *new2 = parcByteArray_Acquire(actual); + assertTrue(new2 == actual, "Expected new to be the same as actual"); + + parcByteArray_Release(&new1); + assertNull(new1, "Expected destroy to null the pointer"); + assertNotNull(actual, "Expected destroy to NOT null the original pointer"); + + parcByteArray_Release(&new2); + assertNull(new1, "Expected destroy to null the pointer"); + assertNotNull(actual, "Expected destroy to NOT null the original pointer"); + + parcByteArray_Release(&reference); + parcByteArray_Release(&actual); +} + +LONGBOW_TEST_CASE(Global, parcByteArray_Acquire_destroyoriginal) +{ + uint8_t buffer[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCByteArray *original = parcByteArray_Wrap(10, buffer); + + PARCByteArray *ref1 = parcByteArray_Acquire(original); + assertTrue(ref1 == original, "Expected new to be the same as original"); + + parcByteArray_Release(&original); + assertNull(original, "Expected destroy to null the pointer"); + assertNotNull(ref1, "Expected destroy to NOT null the new reference"); + + parcByteArray_Release(&ref1); +} + +LONGBOW_TEST_CASE(Global, parcByteArray_HashCode) +{ + uint8_t buffer[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCByteArray *x = parcByteArray_Wrap(10, buffer); + PARCByteArray *y = parcByteArray_Wrap(10, buffer); + + PARCHashCode hashX = parcByteArray_HashCode(x); + PARCHashCode hashY = parcByteArray_HashCode(y); + + assertTrue(hashX == hashY, + "Expected %" PRIPARCHashCode ", actual %" PRIPARCHashCode, hashX, hashY); + + parcByteArray_Release(&x); + parcByteArray_Release(&y); +} + +LONGBOW_TEST_CASE(Global, parcByteArray_Display) +{ + uint8_t buffer[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 }; + + PARCByteArray *x = parcByteArray_Wrap(sizeof(buffer), buffer); + + parcByteArray_Display(x, 0); + + parcByteArray_Release(&x); +} + +LONGBOW_TEST_FIXTURE(Errors) +{ + LONGBOW_RUN_TEST_CASE(Errors, parcByteArray_Put_overrun); + LONGBOW_RUN_TEST_CASE(Errors, parcByteArray_Get_overrun); + LONGBOW_RUN_TEST_CASE(Errors, parcByteArray_CopyIn_overrun); + LONGBOW_RUN_TEST_CASE(Errors, parcByteArray_CopyOut_overrun); +} + +typedef struct parc_byte_array_longbow_clipboard { + PARCByteArray *byteArray; +} parcByteArray_LongBowClipBoard; + +LONGBOW_TEST_FIXTURE_SETUP(Errors) +{ + parcByteArray_LongBowClipBoard *clipboardData = calloc(1, sizeof(parcByteArray_LongBowClipBoard)); + clipboardData->byteArray = parcByteArray_Allocate(10); + + longBowTestCase_SetClipBoardData(testCase, clipboardData); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Errors) +{ + parcByteArray_LongBowClipBoard *clipboardData = longBowTestCase_GetClipBoardData(testCase); + parcByteArray_Release(&clipboardData->byteArray); + free(clipboardData); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("Errors %s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE_EXPECTS(Errors, parcByteArray_Put_overrun, .event = &LongBowTrapOutOfBounds) +{ + parcByteArray_LongBowClipBoard *testData = longBowTestCase_GetClipBoardData(testCase); + PARCByteArray *original = testData->byteArray; + + for (uint8_t index = 0; index < 10 + 1; index++) { + parcByteArray_PutByte(original, index, index); // This will fail. + } +} + +LONGBOW_TEST_CASE_EXPECTS(Errors, parcByteArray_CopyIn_overrun, .event = &LongBowTrapOutOfBounds) +{ + uint8_t actual[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + parcByteArray_LongBowClipBoard *testData = longBowTestCase_GetClipBoardData(testCase); + PARCByteArray *original = testData->byteArray; + parcByteArray_GetBytes(original, 1, 10, actual); // This will fail. +} + +LONGBOW_TEST_CASE_EXPECTS(Errors, parcByteArray_CopyOut_overrun, .event = &LongBowTrapOutOfBounds) +{ + uint8_t actual[10]; + + parcByteArray_LongBowClipBoard *testData = longBowTestCase_GetClipBoardData(testCase); + PARCByteArray *original = testData->byteArray; + parcByteArray_GetBytes(original, 1, 10, actual); // This will fail. +} + +LONGBOW_TEST_CASE_EXPECTS(Errors, parcByteArray_Get_overrun, .event = &LongBowTrapOutOfBounds) +{ + uint8_t buffer[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + parcByteArray_LongBowClipBoard *testData = longBowTestCase_GetClipBoardData(testCase); + PARCByteArray *original = testData->byteArray; + parcByteArray_PutBytes(original, 0, 10, buffer); + + for (uint8_t index = 0; index < 10 + 1; index++) { + uint8_t actual = parcByteArray_GetByte(original, index); // this will fail. + assertTrue(index == actual, "Expected %d, actual %d", index, actual); + } +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(PARCByteArray); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_Chunker.c b/libparc/parc/algol/test/test_parc_Chunker.c new file mode 100755 index 00000000..945e6bab --- /dev/null +++ b/libparc/parc/algol/test/test_parc_Chunker.c @@ -0,0 +1,271 @@ +/* + * 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 the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../parc_Chunker.c" + +#include <stdio.h> + +#include <parc/algol/parc_Object.h> + +#include <LongBow/unit-test.h> +#include <parc/algol/parc_SafeMemory.h> + +typedef struct { + int val; + int dir; + bool atEnd; +} _DummyChunkerState; + +typedef struct { + int start; + int end; + bool released; + size_t chunkSize; +} _DummyChunker; + +static void * +_InitForward(_DummyChunker *chunker) +{ + _DummyChunkerState *state = parcMemory_Allocate(sizeof(_DummyChunkerState)); + + state->val = 0; + state->dir = 1; + state->atEnd = false; + + return state; +} + +static bool +_hasNext(_DummyChunker *chunker, void *voidstate) +{ + _DummyChunkerState *state = (_DummyChunkerState *) voidstate; + return !state->atEnd; +} + +static void * +_next(_DummyChunker *chunker, void *voidstate) +{ + _DummyChunkerState *state = (_DummyChunkerState *) voidstate; + state->val++; + + if (state->val == chunker->end) { + state->atEnd = true; + } + + return state; +} + +static void * +_get(_DummyChunker *chunker, void *voidstate) +{ + _DummyChunkerState *state = (_DummyChunkerState *) voidstate; + return &(state->val); +} + +static void +_finish(_DummyChunker *chunker, void *state) +{ + _DummyChunkerState *thestate = (_DummyChunkerState *) state; + parcMemory_Deallocate(&thestate); +} + +static PARCIterator * +_mock_ForwardIterator(const void *chunker) +{ + PARCIterator *iterator = parcIterator_Create((void *) chunker, + (void *(*)(PARCObject *))_InitForward, + (bool (*)(PARCObject *, void *))_hasNext, + (void *(*)(PARCObject *, void *))_next, + NULL, + (void *(*)(PARCObject *, void *))_get, + (void (*)(void *, void *))_finish, + NULL); + + return iterator; +} + +static PARCIterator * +_mock_ReverseIterator(const void *chunker) +{ + PARCIterator *iterator = parcIterator_Create((void *) chunker, + (void *(*)(PARCObject *))_InitForward, + (bool (*)(PARCObject *, void *))_hasNext, + (void *(*)(PARCObject *, void *))_next, + NULL, + (void *(*)(PARCObject *, void *))_get, + (void (*)(void *, void *))_finish, + NULL); + + return iterator; +} + +static size_t +_mock_GetChunkSize(const void *chunker) +{ + _DummyChunker *dummy = (_DummyChunker *) chunker; + return dummy->chunkSize; +} + +static void +_dummyDestroy(_DummyChunker **chunkerP) +{ + // pass... +} + +PARCChunkerInterface *_MockChunker = &(PARCChunkerInterface) { + .ForwardIterator = (void *(*)(const void *))_mock_ForwardIterator, + .ReverseIterator = (void *(*)(const void *))_mock_ReverseIterator, + .GetChunkSize = (size_t (*)(const void *))_mock_GetChunkSize +}; + +parcObject_ExtendPARCObject(_DummyChunker, _dummyDestroy, NULL, NULL, NULL, NULL, NULL, NULL); +parcObject_ImplementAcquire(_dummy, _DummyChunker); +parcObject_ImplementRelease(_dummy, _DummyChunker); + +static _DummyChunker * +_dummy_Create(int val) +{ + _DummyChunker *chunker = (_DummyChunker *) parcObject_CreateAndClearInstance(_DummyChunker); + chunker->start = 0; + chunker->end = val; + chunker->released = false; + chunker->chunkSize = val; + return chunker; +} + +LONGBOW_TEST_RUNNER(parc_Chunker) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_Chunker) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_Chunker) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parc_Chunker_Create); + LONGBOW_RUN_TEST_CASE(Global, parc_Chunker_ForwardIterator); + LONGBOW_RUN_TEST_CASE(Global, parc_Chunker_ReverseIterator); + LONGBOW_RUN_TEST_CASE(Global, parc_Chunker_GetChunkSize); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parc_Chunker_Create) +{ + _DummyChunker *dummy = _dummy_Create(10); + PARCChunker *chunker = parcChunker_Create(dummy, _MockChunker); + _dummy_Release(&dummy); + + assertNotNull(chunker, "Expected non-NULL PARCChunker to be created from the dummy MockChunker"); + PARCChunker *copy = parcChunker_Acquire(chunker); + assertNotNull(copy, "Expected non-NULL copy of the PARCChunker"); + + parcChunker_Release(&chunker); + parcChunker_Release(©); +} + +LONGBOW_TEST_CASE(Global, parc_Chunker_ForwardIterator) +{ + int n = 10; + + _DummyChunker *dummy = _dummy_Create(n); + PARCChunker *chunker = parcChunker_Create(dummy, _MockChunker); + _dummy_Release(&dummy); + PARCIterator *itr = parcChunker_ForwardIterator(chunker); + + int targetSum = (n * (n + 1)) / 2; + int sum = 0; + while (parcIterator_HasNext(itr)) { + int *val = parcIterator_Next(itr); + sum += *val; + } + assertTrue(targetSum == sum, "Expected the iterator to walk the chunker as needed\n"); + + parcIterator_Release(&itr); + parcChunker_Release(&chunker); +} + +LONGBOW_TEST_CASE(Global, parc_Chunker_ReverseIterator) +{ + int n = 10; + + _DummyChunker *dummy = _dummy_Create(n); + PARCChunker *chunker = parcChunker_Create(dummy, _MockChunker); + _dummy_Release(&dummy); + PARCIterator *itr = parcChunker_ReverseIterator(chunker); + + int targetSum = (n * (n + 1)) / 2; + int sum = 0; + while (parcIterator_HasNext(itr)) { + int *val = parcIterator_Next(itr); + sum += *val; + } + assertTrue(targetSum == sum, "Expected the iterator to walk the chunker as needed\n"); + + parcIterator_Release(&itr); + parcChunker_Release(&chunker); +} + +LONGBOW_TEST_CASE(Global, parc_Chunker_GetChunkSize) +{ + int n = 10; + + _DummyChunker *dummy = _dummy_Create(n); + PARCChunker *chunker = parcChunker_Create(dummy, _MockChunker); + _dummy_Release(&dummy); + + size_t chunkSize = parcChunker_GetChunkSize(chunker); + assertTrue(chunkSize == n, "Expected the chunk size to be %d, got %zu\n", n, chunkSize); + + parcChunker_Release(&chunker); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_Chunker); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_Clock.c b/libparc/parc/algol/test/test_parc_Clock.c new file mode 100644 index 00000000..0adf813e --- /dev/null +++ b/libparc/parc/algol/test/test_parc_Clock.c @@ -0,0 +1,198 @@ +/* + * 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 the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../parc_Clock.c" +#include <stdio.h> +#include <LongBow/unit-test.h> +#include <parc/algol/parc_SafeMemory.h> + +LONGBOW_TEST_RUNNER(parc_Clock) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_Clock) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_Clock) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// ========================================================== + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcClock_Wallclock); + LONGBOW_RUN_TEST_CASE(Global, parcClock_Wallclock_Acquire); + LONGBOW_RUN_TEST_CASE(Global, parcClock_Wallclock_GetTime); + LONGBOW_RUN_TEST_CASE(Global, parcClock_Wallclock_GetTimeval); + LONGBOW_RUN_TEST_CASE(Global, parcClock_Montonic); + LONGBOW_RUN_TEST_CASE(Global, parcClock_Monotonic_Acquire); + LONGBOW_RUN_TEST_CASE(Global, parcClock_Monotonic_GetTime); + LONGBOW_RUN_TEST_CASE(Global, parcClock_Monotonic_GetTimeval); + + LONGBOW_RUN_TEST_CASE(Global, counterClock_Create); + LONGBOW_RUN_TEST_CASE(Global, counterClock_Acquire); + LONGBOW_RUN_TEST_CASE(Global, counterClock_GetTime); + LONGBOW_RUN_TEST_CASE(Global, counterClock_GetTime_Twice); + LONGBOW_RUN_TEST_CASE(Global, counterClock_GetTimeval); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcClock_Wallclock) +{ + PARCClock *clock = parcClock_Wallclock(); + assertNotNull(clock, "Got null wall clock"); + parcClock_Release(&clock); +} + +LONGBOW_TEST_CASE(Global, parcClock_Wallclock_Acquire) +{ + PARCClock *clock = parcClock_Wallclock(); + PARCClock *copy = parcClock_Acquire(clock); + assertNotNull(copy, "Got null wall clock"); + parcClock_Release(©); + parcClock_Release(&clock); +} + +LONGBOW_TEST_CASE(Global, parcClock_Wallclock_GetTime) +{ + PARCClock *clock = parcClock_Wallclock(); + uint64_t t = parcClock_GetTime(clock); + parcClock_Release(&clock); + assertTrue(t > 0, "got 0 time"); +} + +LONGBOW_TEST_CASE(Global, parcClock_Wallclock_GetTimeval) +{ + PARCClock *clock = parcClock_Wallclock(); + struct timeval tv = { 0, 0 }; + parcClock_GetTimeval(clock, &tv); + parcClock_Release(&clock); + assertTrue(tv.tv_sec > 0, "Got 0 seconds"); +} + +// ----- + +LONGBOW_TEST_CASE(Global, parcClock_Montonic) +{ + PARCClock *clock = parcClock_Monotonic(); + assertNotNull(clock, "Got null wall clock"); + parcClock_Release(&clock); +} + +LONGBOW_TEST_CASE(Global, parcClock_Monotonic_Acquire) +{ + PARCClock *clock = parcClock_Monotonic(); + PARCClock *copy = parcClock_Acquire(clock); + assertNotNull(copy, "Got null wall clock"); +} + +LONGBOW_TEST_CASE(Global, parcClock_Monotonic_GetTime) +{ + PARCClock *clock = parcClock_Monotonic(); + uint64_t t = parcClock_GetTime(clock); + parcClock_Release(&clock); + assertTrue(t > 0, "got 0 time"); +} + +LONGBOW_TEST_CASE(Global, parcClock_Monotonic_GetTimeval) +{ + PARCClock *clock = parcClock_Monotonic(); + struct timeval tv = { 0, 0 }; + parcClock_GetTimeval(clock, &tv); + parcClock_Release(&clock); + assertTrue(tv.tv_sec > 0, "Got 0 seconds"); +} + +// ----- + +LONGBOW_TEST_CASE(Global, counterClock_Create) +{ + PARCClock *clock = parcClock_Counter(); + assertNotNull(clock, "Got null wall clock"); + parcClock_Release(&clock); +} + +LONGBOW_TEST_CASE(Global, counterClock_Acquire) +{ + PARCClock *clock = parcClock_Counter(); + PARCClock *copy = parcClock_Acquire(clock); + assertNotNull(copy, "Got null wall clock"); + parcClock_Release(©); + parcClock_Release(&clock); +} + +LONGBOW_TEST_CASE(Global, counterClock_GetTime) +{ + PARCClock *clock = parcClock_Counter(); + uint64_t t = parcClock_GetTime(clock); + parcClock_Release(&clock); + assertTrue(t == 1, "On first call should have gotten 1"); +} + +LONGBOW_TEST_CASE(Global, counterClock_GetTime_Twice) +{ + PARCClock *clock = parcClock_Counter(); + parcClock_GetTime(clock); + uint64_t t2 = parcClock_GetTime(clock); + parcClock_Release(&clock); + assertTrue(t2 == 2, "On second call should have gotten 2"); +} + +LONGBOW_TEST_CASE(Global, counterClock_GetTimeval) +{ + PARCClock *clock = parcClock_Counter(); + struct timeval tv = { 0, 0 }; + parcClock_GetTimeval(clock, &tv); + parcClock_Release(&clock); + assertTrue(tv.tv_usec == 1, "On first call should have gotten 1 usec"); +} + + +// ========================================================== + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_Clock); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} + diff --git a/libparc/parc/algol/test/test_parc_Deque.c b/libparc/parc/algol/test/test_parc_Deque.c new file mode 100755 index 00000000..02884b4a --- /dev/null +++ b/libparc/parc/algol/test/test_parc_Deque.c @@ -0,0 +1,620 @@ +/* + * 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 "../parc_Deque.c" +#include <LongBow/unit-test.h> +#include <parc/algol/parc_SafeMemory.h> +#include <parc/algol/parc_StdlibMemory.h> + +#include <parc/testing/parc_ObjectTesting.h> +#include <parc/testing/parc_MemoryTesting.h> + +LONGBOW_TEST_RUNNER(parc_Deque) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Local); + LONGBOW_RUN_TEST_FIXTURE(AcquireRelease); + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Performance); +// LONGBOW_RUN_TEST_FIXTURE(Errors); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_Deque) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_Deque) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(AcquireRelease) +{ + LONGBOW_RUN_TEST_CASE(AcquireRelease, parcDeque_CreateRelease); + LONGBOW_RUN_TEST_CASE(AcquireRelease, parcDeque_CreateRelease_WithEquals); + LONGBOW_RUN_TEST_CASE(AcquireRelease, parcDeque_AcquireRelease); +} + +LONGBOW_TEST_FIXTURE_SETUP(AcquireRelease) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(AcquireRelease) +{ + bool leaked = parcMemoryTesting_ExpectedOutstanding(0, "%s leaks memory \n", longBowTestCase_GetName(testCase)) != true; + if (leaked) { + parcSafeMemory_ReportAllocation(STDOUT_FILENO); + printf("bailing\n"); + exit(1); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(AcquireRelease, parcDeque_CreateRelease) +{ + PARCDeque *deque = parcDeque_Create(); + assertNotNull(deque, "Expected non-null result from parcDeque_Create()"); + + parcDeque_Release(&deque); + assertNull(deque, "Expected parcDeque_Release to null the pointer"); +} + +LONGBOW_TEST_CASE(AcquireRelease, parcDeque_CreateRelease_WithEquals) +{ + PARCDeque *deque = parcDeque_CreateCustom(NULL, NULL); + assertNotNull(deque, "Expected non-null result from parcDeque_Create()"); + + parcDeque_Release(&deque); + assertNull(deque, "Expected parcDeque_Release to null the pointer"); +} + +LONGBOW_TEST_CASE(AcquireRelease, parcDeque_AcquireRelease) +{ + PARCDeque *original = parcDeque_Create(); + assertNotNull(original, "Expected non-null result from parcDeque_Create()"); + + parcObjectTesting_AssertAcquireReleaseContract(parcDeque_Acquire, original); + + PARCDeque *reference = parcDeque_Acquire(original); + assertTrue(original == reference, "Expected the reference to be equal to the original."); + + parcDeque_Release(&original); + assertNull(original, "Expected parcDeque_Release to null the pointer"); + + parcDeque_Append(reference, (void *) 1); + size_t expected = 1; + size_t actual = parcDeque_Size(reference); + assertTrue(expected == actual, + "Expected size %zd, actual %zd", expected, actual); + parcDeque_Release(&reference); +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcDeque_Append_One); + LONGBOW_RUN_TEST_CASE(Global, parcDeque_Append_Two); + LONGBOW_RUN_TEST_CASE(Global, parcDeque_CreateDestroy); + LONGBOW_RUN_TEST_CASE(Global, parcDeque_PeekFirst); + LONGBOW_RUN_TEST_CASE(Global, parcDeque_PeekLast); + + LONGBOW_RUN_TEST_CASE(Global, parcDeque_Prepend_One); + LONGBOW_RUN_TEST_CASE(Global, parcDeque_Prepend_Two); + LONGBOW_RUN_TEST_CASE(Global, parcDeque_Prepend_Three); + + LONGBOW_RUN_TEST_CASE(Global, parcDeque_IsEmpty); + LONGBOW_RUN_TEST_CASE(Global, parcDeque_GetAtIndex); + + LONGBOW_RUN_TEST_CASE(Global, parcDeque_RemoveFirst); + LONGBOW_RUN_TEST_CASE(Global, parcDeque_RemoveFirst_SingleElement); + LONGBOW_RUN_TEST_CASE(Global, parcDeque_RemoveLast); + LONGBOW_RUN_TEST_CASE(Global, parcDeque_Size); + LONGBOW_RUN_TEST_CASE(Global, parcDeque_Equals); + LONGBOW_RUN_TEST_CASE(Global, parcDeque_Copy); + LONGBOW_RUN_TEST_CASE(Global, parcDeque_Display); + LONGBOW_RUN_TEST_CASE(Global, parcDeque_Display_NULL); + + LONGBOW_RUN_TEST_CASE(Global, parcDeque_Iterator); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + bool leaked = parcMemoryTesting_ExpectedOutstanding(0, "%s leaks memory \n", longBowTestCase_GetName(testCase)) != true; + if (leaked) { + parcSafeMemory_ReportAllocation(STDOUT_FILENO); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcDeque_Append_One) +{ + PARCDeque *deque = parcDeque_Create(); + PARCDeque *actual = parcDeque_Append(deque, "element 1"); + + assertTrue(deque == actual, "Expected parcDeque_Append to return its argument."); + assertTrue(parcDeque_Size(deque) == 1, "Expected size of 1, actual %zd", parcDeque_Size(deque)); + + parcDeque_Release(&deque); +} + +LONGBOW_TEST_CASE(Global, parcDeque_Append_Two) +{ + PARCDeque *deque = parcDeque_Create(); + parcDeque_Append(deque, "element 1"); + PARCDeque *actual = parcDeque_Append(deque, "element 2"); + + assertTrue(deque == actual, "Expected parcDeque_Append to return its argument."); + assertTrue(parcDeque_Size(deque) == 2, "Expected size of 2, actual %zd", parcDeque_Size(deque)); + + parcDeque_Release(&deque); +} + +LONGBOW_TEST_CASE(Global, parcDeque_CreateDestroy) +{ + PARCDeque *deque = parcDeque_Create(); + assertNotNull(deque, "Expected non-null result from parcDeque_Create()"); + + parcDeque_Release(&deque); + assertNull(deque, "Expected parcDeque_Destroy to null the pointer"); +} + +LONGBOW_TEST_CASE(Global, parcDeque_PeekFirst) +{ + char *expected = "element 2"; + PARCDeque *deque = parcDeque_Create(); + parcDeque_Append(deque, expected); + parcDeque_Append(deque, "element 2"); + parcDeque_Append(deque, "element 3"); + + char *actual = parcDeque_PeekFirst(deque); + assertTrue(strcmp(expected, actual) == 0, "Expected '%s' actual '%s'", expected, actual); + + parcDeque_Release(&deque); +} + +LONGBOW_TEST_CASE(Global, parcDeque_PeekLast) +{ + char *expected = "element 2"; + PARCDeque *deque = parcDeque_Create(); + parcDeque_Append(deque, "element 1"); + parcDeque_Append(deque, "element 2"); + parcDeque_Append(deque, expected); + + char *actual = parcDeque_PeekLast(deque); + assertTrue(strcmp(expected, actual) == 0, "Expected '%s' actual '%s'", expected, actual); + + parcDeque_Release(&deque); +} + +LONGBOW_TEST_CASE(Global, parcDeque_Prepend_One) +{ + PARCDeque *deque = parcDeque_Create(); + PARCDeque *actual = parcDeque_Prepend(deque, "element 1"); + + assertTrue(deque == actual, "Expected parcDeque_Append to return its argument."); + assertTrue(parcDeque_Size(deque) == 1, "Expected size of 1, actual %zd", parcDeque_Size(deque)); + assertTrue(deque->head != NULL, "Expected head to be not null."); + assertTrue(deque->head == deque->tail, "Expected head to be equal to the tail."); + + parcDeque_Release(&deque); +} + +LONGBOW_TEST_CASE(Global, parcDeque_Prepend_Two) +{ + PARCDeque *deque = parcDeque_Create(); + parcDeque_Prepend(deque, "element 2"); + PARCDeque *actual = parcDeque_Prepend(deque, "element 1"); + + assertTrue(deque == actual, "Expected parcDeque_Prepend to return its argument."); + assertTrue(parcDeque_Size(deque) == 2, "Expected size of 2, actual %zd", parcDeque_Size(deque)); + + parcDeque_Release(&deque); +} + +LONGBOW_TEST_CASE(Global, parcDeque_Prepend_Three) +{ + char *expectedFirst = "expected first"; + char *expectedLast = "expected last"; + + PARCDeque *deque = parcDeque_Create(); + parcDeque_Prepend(deque, expectedLast); + parcDeque_Prepend(deque, "element 2"); + PARCDeque *actual = parcDeque_Prepend(deque, expectedFirst); + + assertTrue(deque == actual, "Expected parcDeque_Prepend to return its argument."); + assertTrue(parcDeque_Size(deque) == 3, "Expected size of 3, actual %zd", parcDeque_Size(deque)); + + char *peek = parcDeque_PeekFirst(deque); + assertTrue(strcmp(expectedFirst, peek) == 0, "Expected '%s' actual '%s'", expectedFirst, peek); + + peek = parcDeque_PeekLast(deque); + assertTrue(strcmp(expectedLast, peek) == 0, "Expected '%s' actual '%s'", expectedLast, peek); + + parcDeque_Release(&deque); +} + +LONGBOW_TEST_CASE(Global, parcDeque_RemoveFirst) +{ + char *expectedFirst = "expected 1st"; + char *expectedLast = "expected last"; + + PARCDeque *deque = parcDeque_Create(); + parcDeque_Prepend(deque, expectedLast); + parcDeque_Prepend(deque, "element 2"); + parcDeque_Prepend(deque, expectedFirst); + + char *peek = parcDeque_RemoveFirst(deque); + assertTrue(strcmp(expectedFirst, peek) == 0, "Expected '%s' actual '%s'", expectedFirst, peek); + + parcDeque_Release(&deque); +} + +LONGBOW_TEST_CASE(Global, parcDeque_RemoveFirst_SingleElement) +{ + char *expectedFirst = "expected 1st"; + + PARCDeque *deque = parcDeque_Create(); + parcDeque_Prepend(deque, expectedFirst); + + char *peek = parcDeque_RemoveFirst(deque); + assertTrue(strcmp(expectedFirst, peek) == 0, + "Expected '%s' actual '%s'", expectedFirst, peek); + + parcDeque_Release(&deque); +} + +LONGBOW_TEST_CASE(Global, parcDeque_RemoveLast) +{ + char *expectedFirst = "expected 1st"; + char *expectedLast = "expected last"; + + PARCDeque *deque = parcDeque_Create(); + parcDeque_Prepend(deque, expectedLast); + parcDeque_Prepend(deque, "element 2"); + parcDeque_Prepend(deque, expectedFirst); + + char *peek = parcDeque_RemoveLast(deque); + assertTrue(strcmp(expectedLast, peek) == 0, + "Expected '%s' actual '%s'", expectedLast, peek); + + parcDeque_Release(&deque); +} + +LONGBOW_TEST_CASE(Global, parcDeque_RemoveLast_SingleElement) +{ + char *expectedFirst = "expected 1st"; + + PARCDeque *deque = parcDeque_Create(); + parcDeque_Prepend(deque, expectedFirst); + + char *peek = parcDeque_RemoveLast(deque); + assertTrue(strcmp(expectedFirst, peek) == 0, + "Expected '%s' actual '%s'", expectedFirst, peek); + + parcDeque_Release(&deque); +} + +LONGBOW_TEST_CASE(Global, parcDeque_Size) +{ + char *expectedFirst = "expected 1st"; + char *expectedLast = "expected last"; + + PARCDeque *deque = parcDeque_Create(); + parcDeque_Prepend(deque, expectedLast); + parcDeque_Prepend(deque, "element 2"); + parcDeque_Prepend(deque, expectedFirst); + + assertTrue(parcDeque_Size(deque) == 3, + "Expected 3, actual %zd", parcDeque_Size(deque)); + parcDeque_Release(&deque); +} + +LONGBOW_TEST_CASE(Global, parcDeque_IsEmpty) +{ + char *expectedLast = "expected last"; + + PARCDeque *deque = parcDeque_Create(); + + assertTrue(parcDeque_IsEmpty(deque), "Expected true."); + parcDeque_Prepend(deque, expectedLast); + assertFalse(parcDeque_IsEmpty(deque), "Expected false."); + parcDeque_Release(&deque); +} + +LONGBOW_TEST_CASE(Global, parcDeque_GetAtIndex) +{ + char *expected0 = "expected 1"; + char *expected1 = "expected 2"; + char *expected2 = "expected 3"; + + PARCDeque *deque = parcDeque_Create(); + parcDeque_Append(deque, expected0); + parcDeque_Append(deque, expected1); + parcDeque_Append(deque, expected2); + + char *actual; + actual = parcDeque_GetAtIndex(deque, 0); + assertTrue(strcmp(actual, expected0) == 0, "Expected '%s', actual '%s", expected0, actual); + actual = parcDeque_GetAtIndex(deque, 1); + assertTrue(strcmp(actual, expected1) == 0, "Expected '%s', actual '%s", expected1, actual); + actual = parcDeque_GetAtIndex(deque, 2); + assertTrue(strcmp(actual, expected2) == 0, "Expected '%s', actual '%s", expected2, actual); + + parcDeque_Release(&deque); +} + +LONGBOW_TEST_CASE(Global, parcDeque_Equals) +{ + PARCDeque *x = parcDeque_Create(); + parcDeque_Append(x, (void *) 0); + parcDeque_Append(x, (void *) 1); + parcDeque_Append(x, (void *) 2); + parcDeque_Append(x, (void *) 3); + parcDeque_Append(x, (void *) 4); + parcDeque_Append(x, (void *) 5); + PARCDeque *y = parcDeque_Create(); + parcDeque_Append(y, (void *) 0); + parcDeque_Append(y, (void *) 1); + parcDeque_Append(y, (void *) 2); + parcDeque_Append(y, (void *) 3); + parcDeque_Append(y, (void *) 4); + parcDeque_Append(y, (void *) 5); + PARCDeque *z = parcDeque_Create(); + parcDeque_Append(z, (void *) 0); + parcDeque_Append(z, (void *) 1); + parcDeque_Append(z, (void *) 2); + parcDeque_Append(z, (void *) 3); + parcDeque_Append(z, (void *) 4); + parcDeque_Append(z, (void *) 5); + PARCDeque *u1 = parcDeque_Create(); + parcDeque_Append(u1, (void *) 0); + parcDeque_Append(u1, (void *) 1); + parcDeque_Append(u1, (void *) 2); + parcDeque_Append(u1, (void *) 3); + parcDeque_Append(u1, (void *) 4); + PARCDeque *u2 = parcDeque_Create(); + parcDeque_Append(u2, (void *) 0); + parcDeque_Append(u2, (void *) 1); + parcDeque_Append(u2, (void *) 2); + parcDeque_Append(u2, (void *) 3); + parcDeque_Append(u2, (void *) 4); + parcDeque_Append(u2, (void *) 4); + + parcObjectTesting_AssertEqualsFunction(parcDeque_Equals, x, y, z, u1, u2, NULL); + + parcDeque_Release(&x); + parcDeque_Release(&y); + parcDeque_Release(&z); + parcDeque_Release(&u1); + parcDeque_Release(&u2); +} + +LONGBOW_TEST_CASE(Global, parcDeque_Copy) +{ + PARCDeque *x = parcDeque_Create(); + parcDeque_Append(x, (void *) 0); + parcDeque_Append(x, (void *) 1); + parcDeque_Append(x, (void *) 2); + parcDeque_Append(x, (void *) 3); + parcDeque_Append(x, (void *) 4); + parcDeque_Append(x, (void *) 5); + + PARCDeque *y = parcDeque_Copy(x); + + assertTrue(parcDeque_Equals(x, y), "Expected the copy to be equal to the original."); + + parcDeque_Release(&y); + parcDeque_Release(&x); +} + +LONGBOW_TEST_CASE(Global, parcDeque_Display) +{ + PARCDeque *x = parcDeque_Create(); + parcDeque_Append(x, (void *) 0); + parcDeque_Append(x, (void *) 1); + parcDeque_Append(x, (void *) 2); + parcDeque_Append(x, (void *) 3); + parcDeque_Append(x, (void *) 4); + parcDeque_Append(x, (void *) 5); + + parcDeque_Display(x, 0); + + parcDeque_Release(&x); +} + +LONGBOW_TEST_CASE(Global, parcDeque_Display_NULL) +{ + parcDeque_Display(NULL, 0); +} + +LONGBOW_TEST_CASE(Global, parcDeque_Iterator) +{ + PARCDeque *x = parcDeque_Create(); + for (size_t i = 0; i < 100; i++) { + parcDeque_Append(x, (void *) i); + } + + PARCIterator *iterator = parcDeque_Iterator(x); + size_t expected = 0; + while (parcIterator_HasNext(iterator)) { + size_t actual = (size_t) parcIterator_Next(iterator); + assertTrue(expected == actual, "Expected %zd, actual %zd", expected, actual); + expected++; + } + parcIterator_Release(&iterator); + + parcDeque_Release(&x); +} + +LONGBOW_TEST_FIXTURE(Local) +{ + LONGBOW_RUN_TEST_CASE(Local, _parcDequeNode_Create); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + bool leaked = parcMemoryTesting_ExpectedOutstanding(0, "%s leaks memory \n", longBowTestCase_GetName(testCase)) != true; + if (leaked) { + parcSafeMemory_ReportAllocation(STDOUT_FILENO); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Local, _parcDequeNode_Create) +{ + void *element = "element"; + struct parc_deque_node *previous = NULL; + struct parc_deque_node *next = NULL; + + struct parc_deque_node *actual = _parcDequeNode_Create(element, previous, next); + _parcDequeNode_Destroy(NULL, &actual); +} + +LONGBOW_TEST_FIXTURE(Errors) +{ + LONGBOW_RUN_TEST_CASE(Errors, parcDeque_GetAtIndex_OutOfBounds); +} + +LONGBOW_TEST_FIXTURE_SETUP(Errors) +{ + PARCDeque *deque = parcDeque_Create(); + longBowTestCase_SetClipBoardData(testCase, deque); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Errors) +{ + PARCDeque *deque = longBowTestCase_GetClipBoardData(testCase); + parcDeque_Release(&deque); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE_EXPECTS(Errors, parcDeque_GetAtIndex_OutOfBounds, .event = &LongBowTrapOutOfBounds) +{ + char *expected0 = "expected 1"; + char *expected1 = "expected 2"; + char *expected2 = "expected 3"; + + PARCDeque *deque = longBowTestCase_GetClipBoardData(testCase); + parcDeque_Append(deque, expected0); + parcDeque_Append(deque, expected1); + parcDeque_Append(deque, expected2); + + parcDeque_GetAtIndex(deque, 3); +} + +LONGBOW_TEST_FIXTURE_OPTIONS(Performance, .enabled = false) +{ + LONGBOW_RUN_TEST_CASE(Performance, parcQueue_Append); + LONGBOW_RUN_TEST_CASE(Performance, parcQueue_N2); + LONGBOW_RUN_TEST_CASE(Performance, parcQueue_Iterator); +} + +LONGBOW_TEST_FIXTURE_SETUP(Performance) +{ + parcMemory_SetInterface(&PARCStdlibMemoryAsPARCMemory); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Performance) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Performance, parcQueue_Append) +{ + PARCDeque *x = parcDeque_Create(); + + for (size_t i = 0; i < 100000; i++) { + parcDeque_Append(x, (void *) i); + } + + parcDeque_Release(&x); +} + +LONGBOW_TEST_CASE(Performance, parcQueue_N2) +{ + PARCDeque *x = parcDeque_Create(); + for (size_t i = 0; i < 100000; i++) { + parcDeque_Append(x, (void *) i); + } + + for (size_t expected = 0; expected < parcDeque_Size(x); expected++) { + size_t actual = (size_t) parcDeque_GetAtIndex(x, expected); + assertTrue(expected == actual, "Expected %zd, actual %zd", expected, actual); + } + + parcDeque_Release(&x); +} + +LONGBOW_TEST_CASE(Performance, parcQueue_Iterator) +{ + PARCDeque *x = parcDeque_Create(); + for (size_t i = 0; i < 100000; i++) { + parcDeque_Append(x, (void *) i); + } + + PARCIterator *iterator = parcDeque_Iterator(x); + size_t expected = 0; + while (parcIterator_HasNext(iterator)) { + size_t actual = (size_t) parcIterator_Next(iterator); + assertTrue(expected == actual, "Expected %zd, actual %zd", expected, actual); + expected++; + } + parcIterator_Release(&iterator); + + parcDeque_Release(&x); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_Deque); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_Dictionary.c b/libparc/parc/algol/test/test_parc_Dictionary.c new file mode 100644 index 00000000..095f3c98 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_Dictionary.c @@ -0,0 +1,736 @@ +/* + * 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 <stdio.h> + +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +#include "../parc_Dictionary.c" + +static int * +_keyNewInt(int key) +{ + int *newKey = parcMemory_Allocate(sizeof(int)); + assertNotNull(newKey, "parcMemory_Allocate(%zu) returned NULL", + sizeof(int)); + *newKey = key; + return newKey; +} + +static int * +_valueNewInt(int value) +{ + int *newValue = parcMemory_Allocate(sizeof(int)); + assertNotNull(newValue, "parcMemory_Allocate(%zu) returned NULL", sizeof(int)); + *newValue = value; + return newValue; +} + +static bool +_valueEquals(const void *value1, const void *value2) +{ + return *(int *) value1 == *(int *) value2; +} + +static int +_intKeyComp(const void *key1, const void *key2) +{ + if (*(int *) key1 < *(int *) key2) { + return -1; + } + if (*(int *) key1 == *(int *) key2) { + return 0; + } + return 1; +} + +static uint32_t +_intKeyHash(const void *key1) +{ + return *(int *) key1; +} + + +static void +_keyFree(void **value) +{ + parcMemory_Deallocate((void **) value); + *value = NULL; +} + +static void +_valueFree(void **key) +{ + parcMemory_Deallocate((void **) key); + *key = NULL; +} + + + + +LONGBOW_TEST_RUNNER(PARC_Dictionary) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +LONGBOW_TEST_RUNNER_SETUP(PARC_Dictionary) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_RUNNER_TEARDOWN(PARC_Dictionary) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(-1); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestRunner_GetName(testRunner), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, PARC_Dictionary_Create); + LONGBOW_RUN_TEST_CASE(Global, PARC_Dictionary_SetValue_Destroy); + LONGBOW_RUN_TEST_CASE(Global, PARC_Dictionary_Size_Empty); + LONGBOW_RUN_TEST_CASE(Global, PARC_Dictionary_Size); + LONGBOW_RUN_TEST_CASE(Global, PARC_Dictionary_Size_AfterDelete); + LONGBOW_RUN_TEST_CASE(Global, PARC_Dictionary_Size_AfterOverwrite); + LONGBOW_RUN_TEST_CASE(Global, PARC_Dictionary_Get_EmptyTree); + LONGBOW_RUN_TEST_CASE(Global, PARC_Dictionary_Get_NonExistent); + LONGBOW_RUN_TEST_CASE(Global, PARC_Dictionary_Get_First); + LONGBOW_RUN_TEST_CASE(Global, PARC_Dictionary_Get); + LONGBOW_RUN_TEST_CASE(Global, PARC_Dictionary_Get_Last); + LONGBOW_RUN_TEST_CASE(Global, PARC_Dictionary_Remove_First); + LONGBOW_RUN_TEST_CASE(Global, PARC_Dictionary_Remove); + LONGBOW_RUN_TEST_CASE(Global, PARC_Dictionary_Remove_Last); + LONGBOW_RUN_TEST_CASE(Global, PARC_Dictionary_RemoveAndDestroy_First); + LONGBOW_RUN_TEST_CASE(Global, PARC_Dictionary_RemoveAndDestroy); + LONGBOW_RUN_TEST_CASE(Global, PARC_Dictionary_RemoveAndDestroy_Last); + LONGBOW_RUN_TEST_CASE(Global, PARC_Dictionary_Keys); + LONGBOW_RUN_TEST_CASE(Global, PARC_Dictionary_Values); + LONGBOW_RUN_TEST_CASE(Global, PARC_Dictionary_Equals_Empty); + LONGBOW_RUN_TEST_CASE(Global, PARC_Dictionary_Equals); + LONGBOW_RUN_TEST_CASE(Global, PARC_Dictionary_Equals_Not_Values); + LONGBOW_RUN_TEST_CASE(Global, PARC_Dictionary_Equals_Not_Keys); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, PARC_Dictionary_Create) +{ + PARCDictionary *dictionary = parcDictionary_Create(_intKeyComp, _intKeyHash, NULL, NULL, NULL); + + parcDictionary_Destroy(&dictionary); + + dictionary = parcDictionary_Create(_intKeyComp, _intKeyHash, _keyFree, _valueEquals, _valueFree); + + parcDictionary_Destroy(&dictionary); +} + +LONGBOW_TEST_CASE(Global, PARC_Dictionary_SetValue_Destroy) +{ + PARCDictionary *dictionary; + + dictionary = parcDictionary_Create(_intKeyComp, _intKeyHash, _keyFree, _valueEquals, _valueFree); + + parcDictionary_SetValue(dictionary, (void *) _keyNewInt(1), (void *) _valueNewInt(11)); + parcDictionary_SetValue(dictionary, (void *) _keyNewInt(2), (void *) _valueNewInt(12)); + parcDictionary_SetValue(dictionary, (void *) _keyNewInt(3), (void *) _valueNewInt(13)); + + parcDictionary_Destroy(&dictionary); +} + +LONGBOW_TEST_CASE(Global, PARC_Dictionary_Size_Empty) +{ + PARCDictionary *dictionary; + + dictionary = parcDictionary_Create(_intKeyComp, _intKeyHash, _keyFree, _valueEquals, _valueFree); + + assertTrue(0 == parcDictionary_Size(dictionary), "Wrong size of dictionary - empty, start"); + + parcDictionary_Destroy(&dictionary); +} + +LONGBOW_TEST_CASE(Global, PARC_Dictionary_Size) +{ + PARCDictionary *dictionary; + + dictionary = parcDictionary_Create(_intKeyComp, _intKeyHash, _keyFree, _valueEquals, _valueFree); + + parcDictionary_SetValue(dictionary, (void *) _keyNewInt(4), (void *) _valueNewInt(1004)); + parcDictionary_SetValue(dictionary, (void *) _keyNewInt(3), (void *) _valueNewInt(1003)); + parcDictionary_SetValue(dictionary, (void *) _keyNewInt(2), (void *) _valueNewInt(1002)); + parcDictionary_SetValue(dictionary, (void *) _keyNewInt(8), (void *) _valueNewInt(1008)); + + assertTrue(4 == parcDictionary_Size(dictionary), "Wrong size of dictionary after add 3"); + + parcDictionary_Destroy(&dictionary); +} + +LONGBOW_TEST_CASE(Global, PARC_Dictionary_Size_AfterDelete) +{ + PARCDictionary *dictionary; + + dictionary = parcDictionary_Create(_intKeyComp, _intKeyHash, _keyFree, _valueEquals, _valueFree); + + parcDictionary_SetValue(dictionary, (void *) _keyNewInt(4), (void *) _valueNewInt(1004)); + parcDictionary_SetValue(dictionary, (void *) _keyNewInt(3), (void *) _valueNewInt(1003)); + parcDictionary_SetValue(dictionary, (void *) _keyNewInt(2), (void *) _valueNewInt(1002)); + parcDictionary_SetValue(dictionary, (void *) _keyNewInt(8), (void *) _valueNewInt(1008)); + + int searchKey = 2; + + parcDictionary_RemoveAndDestroyValue(dictionary, &searchKey); + + size_t size = parcDictionary_Size(dictionary); + + assertTrue(3 == size, "Wrong size of dictionary after 1 delete (%zu instead of 3)", size); + + parcDictionary_Destroy(&dictionary); +} + +LONGBOW_TEST_CASE(Global, PARC_Dictionary_Size_AfterOverwrite) +{ + PARCDictionary *dictionary; + + dictionary = parcDictionary_Create(_intKeyComp, _intKeyHash, _keyFree, _valueEquals, _valueFree); + + parcDictionary_SetValue(dictionary, (void *) _keyNewInt(4), (void *) _valueNewInt(1004)); + parcDictionary_SetValue(dictionary, (void *) _keyNewInt(3), (void *) _valueNewInt(1003)); + parcDictionary_SetValue(dictionary, (void *) _keyNewInt(2), (void *) _valueNewInt(1002)); + parcDictionary_SetValue(dictionary, (void *) _keyNewInt(8), (void *) _valueNewInt(1008)); + + parcDictionary_SetValue(dictionary, (void *) _keyNewInt(3), (void *) _valueNewInt(1010)); + + size_t size = parcDictionary_Size(dictionary); + + assertTrue(4 == size, "Wrong size of dictionary after 1 delete (%zu instead of 4)", size); + + parcDictionary_Destroy(&dictionary); +} + +LONGBOW_TEST_CASE(Global, PARC_Dictionary_Get_EmptyTree) +{ + PARCDictionary *dictionary; + + int key = 100; + + dictionary = parcDictionary_Create(_intKeyComp, _intKeyHash, _keyFree, _valueEquals, _valueFree); + + void *value = parcDictionary_GetValue(dictionary, &key); + + assertTrue(NULL == value, "Object did not exist, must return NULL"); + + parcDictionary_Destroy(&dictionary); +} + +LONGBOW_TEST_CASE(Global, PARC_Dictionary_Get_NonExistent) +{ + PARCDictionary *dictionary; + int key = 100; + + dictionary = parcDictionary_Create(_intKeyComp, _intKeyHash, _keyFree, _valueEquals, _valueFree); + + for (int i = 1; i < 10; i++) { + // Add some elements to the dictionary + parcDictionary_SetValue(dictionary, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + } + + void *value = parcDictionary_GetValue(dictionary, &key); + + assertTrue(NULL == value, "Object did not exist, must return NULL"); + + parcDictionary_Destroy(&dictionary); +} + +LONGBOW_TEST_CASE(Global, PARC_Dictionary_Get_First) +{ + PARCDictionary *dictionary; + int key = 1; + + dictionary = parcDictionary_Create(_intKeyComp, _intKeyHash, _keyFree, _valueEquals, _valueFree); + + for (int i = 1; i < 10; i++) { + // Add some elements to the dictionary + parcDictionary_SetValue(dictionary, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + } + + int *value = parcDictionary_GetValue(dictionary, &key); + + assertNotNull(value, "NULL value returned"); + assertTrue(*value == (1 << 8), "Wrong object returned or not found"); + + parcDictionary_Destroy(&dictionary); +} + +LONGBOW_TEST_CASE(Global, PARC_Dictionary_Get) +{ + PARCDictionary *dictionary; + int key = 5; + + dictionary = parcDictionary_Create(_intKeyComp, _intKeyHash, _keyFree, _valueEquals, _valueFree); + + for (int i = 1; i < 10; i++) { + // Add some elements to the dictionary + parcDictionary_SetValue(dictionary, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + } + + int *value = parcDictionary_GetValue(dictionary, &key); + + assertNotNull(value, "NULL value returned"); + assertTrue(*value == (5 << 8), "Wrong object returned or not found"); + + parcDictionary_Destroy(&dictionary); +} + +LONGBOW_TEST_CASE(Global, PARC_Dictionary_Get_Last) +{ + PARCDictionary *dictionary; + int key = 9; + + dictionary = parcDictionary_Create(_intKeyComp, _intKeyHash, _keyFree, _valueEquals, _valueFree); + + for (int i = 1; i < 10; i++) { + // Add some elements to the dictionary + parcDictionary_SetValue(dictionary, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + } + + int *value = parcDictionary_GetValue(dictionary, &key); + + assertNotNull(value, "NULL value returned"); + assertTrue(*value == (9 << 8), "Wrong object returned or not found"); + + parcDictionary_Destroy(&dictionary); +} + +LONGBOW_TEST_CASE(Global, PARC_Dictionary_Remove_First) +{ + PARCDictionary *dictionary1; + PARCDictionary *dictionary2; + + dictionary1 = parcDictionary_Create(_intKeyComp, _intKeyHash, _keyFree, _valueEquals, _valueFree); + dictionary2 = parcDictionary_Create(_intKeyComp, _intKeyHash, _keyFree, _valueEquals, _valueFree); + + for (int i = 30; i < 40; i++) { + // Add some elements to the dictionary + parcDictionary_SetValue(dictionary1, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + parcDictionary_SetValue(dictionary2, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + } + parcDictionary_SetValue(dictionary1, (void *) _keyNewInt(1), (void *) _valueNewInt(1 << 8)); + for (int i = 2; i < 10; i++) { + // Add some elements to the dictionary + parcDictionary_SetValue(dictionary1, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + parcDictionary_SetValue(dictionary2, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + } + for (int i = 20; i < 30; i++) { + // Add some elements to the dictionary + parcDictionary_SetValue(dictionary1, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + parcDictionary_SetValue(dictionary2, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + } + + int searchKey = 1; + + void *data = parcDictionary_RemoveValue(dictionary1, &searchKey); + + _valueFree(&data); + + assertTrue(parcDictionary_Equals(dictionary1, dictionary2), "Trees dont match after remove"); + + parcDictionary_Destroy(&dictionary1); + parcDictionary_Destroy(&dictionary2); +} + +LONGBOW_TEST_CASE(Global, PARC_Dictionary_Remove) +{ + PARCDictionary *dictionary1; + PARCDictionary *dictionary2; + + dictionary1 = parcDictionary_Create(_intKeyComp, _intKeyHash, _keyFree, _valueEquals, _valueFree); + dictionary2 = parcDictionary_Create(_intKeyComp, _intKeyHash, _keyFree, _valueEquals, _valueFree); + + for (int i = 31; i < 40; i++) { + // Add some elements to the dictionary + parcDictionary_SetValue(dictionary1, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + parcDictionary_SetValue(dictionary2, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + } + parcDictionary_SetValue(dictionary1, (void *) _keyNewInt(30), (void *) _valueNewInt(31 << 8)); + for (int i = 2; i < 10; i++) { + // Add some elements to the dictionary + parcDictionary_SetValue(dictionary1, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + parcDictionary_SetValue(dictionary2, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + } + for (int i = 20; i < 30; i++) { + // Add some elements to the dictionary + parcDictionary_SetValue(dictionary1, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + parcDictionary_SetValue(dictionary2, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + } + + int searchKey = 30; + + void *data = parcDictionary_RemoveValue(dictionary1, &searchKey); + + _valueFree(&data); + + assertTrue(parcDictionary_Equals(dictionary1, dictionary2), "Trees dont match after remove"); + + parcDictionary_Destroy(&dictionary1); + parcDictionary_Destroy(&dictionary2); +} + +LONGBOW_TEST_CASE(Global, PARC_Dictionary_Remove_Last) +{ + PARCDictionary *dictionary1; + PARCDictionary *dictionary2; + + dictionary1 = parcDictionary_Create(_intKeyComp, _intKeyHash, _keyFree, _valueEquals, _valueFree); + dictionary2 = parcDictionary_Create(_intKeyComp, _intKeyHash, _keyFree, _valueEquals, _valueFree); + + for (int i = 30; i < 40; i++) { + // Add some elements to the dictionary + parcDictionary_SetValue(dictionary1, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + parcDictionary_SetValue(dictionary2, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + } + parcDictionary_SetValue(dictionary1, (void *) _keyNewInt(100), (void *) _valueNewInt(100 << 8)); + for (int i = 2; i < 10; i++) { + // Add some elements to the dictionary + parcDictionary_SetValue(dictionary1, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + parcDictionary_SetValue(dictionary2, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + } + for (int i = 20; i < 30; i++) { + // Add some elements to the dictionary + parcDictionary_SetValue(dictionary1, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + parcDictionary_SetValue(dictionary2, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + } + + int searchKey = 100; + + void *data = parcDictionary_RemoveValue(dictionary1, &searchKey); + + _valueFree(&data); + + assertTrue(parcDictionary_Equals(dictionary1, dictionary2), "Trees dont match after remove"); + + parcDictionary_Destroy(&dictionary1); + parcDictionary_Destroy(&dictionary2); +} + +LONGBOW_TEST_CASE(Global, PARC_Dictionary_RemoveAndDestroy_First) +{ + PARCDictionary *dictionary1 = parcDictionary_Create(_intKeyComp, _intKeyHash, _keyFree, _valueEquals, _valueFree); + PARCDictionary *dictionary2 = parcDictionary_Create(_intKeyComp, _intKeyHash, _keyFree, _valueEquals, _valueFree); + + for (int i = 30; i < 40; i++) { + // Add some elements to the dictionary + parcDictionary_SetValue(dictionary1, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + parcDictionary_SetValue(dictionary2, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + } + parcDictionary_SetValue(dictionary1, (void *) _keyNewInt(1), (void *) _valueNewInt(1 << 8)); + for (int i = 2; i < 10; i++) { + // Add some elements to the dictionary + parcDictionary_SetValue(dictionary1, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + parcDictionary_SetValue(dictionary2, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + } + for (int i = 20; i < 30; i++) { + // Add some elements to the dictionary + parcDictionary_SetValue(dictionary1, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + parcDictionary_SetValue(dictionary2, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + } + + int searchKey = 1; + + parcDictionary_RemoveAndDestroyValue(dictionary1, &searchKey); + + assertTrue(parcDictionary_Equals(dictionary1, dictionary2), "Trees dont match after remove"); + + parcDictionary_Destroy(&dictionary1); + parcDictionary_Destroy(&dictionary2); +} + +LONGBOW_TEST_CASE(Global, PARC_Dictionary_RemoveAndDestroy) +{ + PARCDictionary *dictionary1; + PARCDictionary *dictionary2; + + dictionary1 = parcDictionary_Create(_intKeyComp, _intKeyHash, _keyFree, _valueEquals, _valueFree); + dictionary2 = parcDictionary_Create(_intKeyComp, _intKeyHash, _keyFree, _valueEquals, _valueFree); + + for (int i = 31; i < 40; i++) { + // Add some elements to the dictionary + parcDictionary_SetValue(dictionary1, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + parcDictionary_SetValue(dictionary2, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + } + parcDictionary_SetValue(dictionary1, (void *) _keyNewInt(30), (void *) _valueNewInt(31 << 8)); + for (int i = 2; i < 10; i++) { + // Add some elements to the dictionary + parcDictionary_SetValue(dictionary1, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + parcDictionary_SetValue(dictionary2, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + } + for (int i = 20; i < 30; i++) { + // Add some elements to the dictionary + parcDictionary_SetValue(dictionary1, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + parcDictionary_SetValue(dictionary2, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + } + + int searchKey = 30; + + parcDictionary_RemoveAndDestroyValue(dictionary1, &searchKey); + + assertTrue(parcDictionary_Equals(dictionary1, dictionary2), "Trees dont match after remove"); + + parcDictionary_Destroy(&dictionary1); + parcDictionary_Destroy(&dictionary2); +} + +LONGBOW_TEST_CASE(Global, PARC_Dictionary_RemoveAndDestroy_Last) +{ + PARCDictionary *dictionary1; + PARCDictionary *dictionary2; + + dictionary1 = parcDictionary_Create(_intKeyComp, _intKeyHash, _keyFree, _valueEquals, _valueFree); + dictionary2 = parcDictionary_Create(_intKeyComp, _intKeyHash, _keyFree, _valueEquals, _valueFree); + + for (int i = 30; i < 40; i++) { + // Add some elements to the dictionary + parcDictionary_SetValue(dictionary1, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + parcDictionary_SetValue(dictionary2, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + } + parcDictionary_SetValue(dictionary1, (void *) _keyNewInt(100), (void *) _valueNewInt(100 << 8)); + for (int i = 2; i < 10; i++) { + // Add some elements to the dictionary + parcDictionary_SetValue(dictionary1, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + parcDictionary_SetValue(dictionary2, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + } + for (int i = 20; i < 30; i++) { + // Add some elements to the dictionary + parcDictionary_SetValue(dictionary1, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + parcDictionary_SetValue(dictionary2, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + } + + int searchKey = 100; + + parcDictionary_RemoveAndDestroyValue(dictionary1, &searchKey); + + assertTrue(parcDictionary_Equals(dictionary1, dictionary2), "Trees dont match after remove"); + + parcDictionary_Destroy(&dictionary1); + parcDictionary_Destroy(&dictionary2); +} + +LONGBOW_TEST_CASE(Global, PARC_Dictionary_Keys) +{ + PARCDictionary *dictionary = + parcDictionary_Create(_intKeyComp, _intKeyHash, _keyFree, _valueEquals, _valueFree); + + for (int i = 0; i < 9; i++) { + // Add some elements to the dictionary + parcDictionary_SetValue(dictionary, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + } + + PARCArrayList *keys = parcDictionary_Keys(dictionary); + + assertNotNull(keys, + "parcDictionary_Keys returned NULL, expected non-NULL."); + + assertTrue(parcArrayList_Size(keys) == parcDictionary_Size(dictionary), + "Expected parcDictionary_Keys size %zu, actual %zd, ", + parcDictionary_Size(dictionary), parcArrayList_Size(keys)); + + for (int i = 0; i < 9; i++) { + bool found = false; + int *keyToFind = _keyNewInt(i); + for (int j = 0; j < parcArrayList_Size(keys); j++) { + int *keyToTest = parcArrayList_Get(keys, j); + if (*keyToTest == *keyToFind) { + found = true; + break; + } + } + assertTrue(found, "Expected to find Key %d, not found", *keyToFind); + parcMemory_Deallocate((void **) &keyToFind); + } + parcArrayList_Destroy(&keys); + parcDictionary_Destroy(&dictionary); +} + +LONGBOW_TEST_CASE(Global, PARC_Dictionary_Values) +{ + PARCDictionary *dictionary = + parcDictionary_Create(_intKeyComp, _intKeyHash, _keyFree, _valueEquals, _valueFree); + + for (int i = 0; i < 9; i++) { + // Add some elements to the dictionary + parcDictionary_SetValue(dictionary, (void *) _keyNewInt(i), (void *) _valueNewInt(i << 8)); + } + + PARCArrayList *values = parcDictionary_Values(dictionary); + + assertNotNull(values, + "parcDictionary_Values returned NULL, expected not NULL"); + + assertTrue(parcArrayList_Size(values) == parcDictionary_Size(dictionary), + "parcDictionary_Values size %zd not equal not parcDictionary_Size, %zd", + parcArrayList_Size(values), parcDictionary_Size(dictionary)); + + for (int i = 0; i < 9; i++) { + bool found = false; + int *keyToFind = _keyNewInt(i); + int *valueToFind = parcDictionary_GetValue(dictionary, keyToFind); + for (int j = 0; j < parcArrayList_Size(values); j++) { + int *valueToTest = parcArrayList_Get(values, j); + if (valueToFind == valueToTest) { + found = true; + break; + } + } + assertTrue(found, + "Expected to find value %d, not found", *valueToFind); + parcMemory_Deallocate((void **) &keyToFind); + } + parcArrayList_Destroy(&values); + parcDictionary_Destroy(&dictionary); +} + + +LONGBOW_TEST_CASE(Global, PARC_Dictionary_Equals_Empty) +{ + PARCDictionary *dictionary1; + PARCDictionary *dictionary2; + + dictionary1 = parcDictionary_Create(_intKeyComp, _intKeyHash, _keyFree, _valueEquals, _valueFree); + dictionary2 = parcDictionary_Create(_intKeyComp, _intKeyHash, _keyFree, _valueEquals, _valueFree); + + assertTrue(parcDictionary_Equals(dictionary1, dictionary2), "Empty lists are not equal"); + + parcDictionary_Destroy(&dictionary1); + parcDictionary_Destroy(&dictionary2); +} + +LONGBOW_TEST_CASE(Global, PARC_Dictionary_Equals_Not_Values) +{ + PARCDictionary *dictionary1; + PARCDictionary *dictionary2; + + int compareSetValues = 100; + + dictionary1 = parcDictionary_Create(_intKeyComp, _intKeyHash, _keyFree, _valueEquals, _valueFree); + dictionary2 = parcDictionary_Create(_intKeyComp, _intKeyHash, _keyFree, _valueEquals, _valueFree); + + printf("Testing dictionary equals...\n"); + + for (int i = 1; i < compareSetValues; i++) { + parcDictionary_SetValue(dictionary1, + (void *) _keyNewInt(i), + (void *) _valueNewInt(i << 8)); + parcDictionary_SetValue(dictionary2, + (void *) _keyNewInt(compareSetValues - i), + (void *) _valueNewInt((compareSetValues + i) << 8)); + } + + assertFalse(parcDictionary_Equals(dictionary1, dictionary2), "Dictionaries are equal and they shouldn't be!"); + + parcDictionary_Destroy(&dictionary1); + parcDictionary_Destroy(&dictionary2); +} + +LONGBOW_TEST_CASE(Global, PARC_Dictionary_Equals_Not_Keys) +{ + PARCDictionary *dictionary1; + PARCDictionary *dictionary2; + + int compareSetValues = 100; + + dictionary1 = parcDictionary_Create(_intKeyComp, _intKeyHash, _keyFree, _valueEquals, _valueFree); + dictionary2 = parcDictionary_Create(_intKeyComp, _intKeyHash, _keyFree, _valueEquals, _valueFree); + + for (int i = 1; i < compareSetValues; i++) { + parcDictionary_SetValue(dictionary1, + (void *) _keyNewInt(i), + (void *) _valueNewInt(i << 8)); + parcDictionary_SetValue(dictionary2, + (void *) _keyNewInt(compareSetValues + i), + (void *) _valueNewInt((compareSetValues - i) << 8)); + } + + assertFalse(parcDictionary_Equals(dictionary1, dictionary2), "Lists are equal"); + + parcDictionary_Destroy(&dictionary1); + parcDictionary_Destroy(&dictionary2); +} + +LONGBOW_TEST_CASE(Global, PARC_Dictionary_Equals) +{ + PARCDictionary *dictionary1; + PARCDictionary *dictionary2; + + int compareSetValues = 100; + + dictionary1 = parcDictionary_Create(_intKeyComp, _intKeyHash, _keyFree, _valueEquals, _valueFree); + dictionary2 = parcDictionary_Create(_intKeyComp, _intKeyHash, _keyFree, _valueEquals, _valueFree); + + for (int i = 1; i < compareSetValues; i++) { + parcDictionary_SetValue(dictionary1, + (void *) _keyNewInt(i), + (void *) _valueNewInt(i << 8)); + parcDictionary_SetValue(dictionary2, + (void *) _keyNewInt(compareSetValues - i), + (void *) _valueNewInt((compareSetValues - i) << 8)); + } + + assertTrue(parcDictionary_Equals(dictionary1, dictionary2), "Dictionaries are not equal"); + + parcDictionary_Destroy(&dictionary1); + parcDictionary_Destroy(&dictionary2); +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(PARC_Dictionary); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_Display.c b/libparc/parc/algol/test/test_parc_Display.c new file mode 100755 index 00000000..29b9fdf2 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_Display.c @@ -0,0 +1,85 @@ +/* + * 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 the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../parc_DisplayIndented.c" + +#include <LongBow/unit-test.h> + +LONGBOW_TEST_RUNNER(parc_Display) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_Display) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_Display) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcDisplay_PrintLine); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcDisplay_PrintLine) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_Display); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_Environment.c b/libparc/parc/algol/test/test_parc_Environment.c new file mode 100755 index 00000000..e4513b31 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_Environment.c @@ -0,0 +1,86 @@ +/* + * 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 the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../parc_Environment.c" + +#include <LongBow/unit-test.h> + +LONGBOW_TEST_RUNNER(parc_Environment) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_Environment) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_Environment) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcEnvironment_GetHomeDirectory); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcEnvironment_GetHomeDirectory) +{ + const char *homeDirectory = parcEnvironment_GetHomeDirectory(); + assertNotNull(homeDirectory, "Cannot get the current home directory."); +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_Environment); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_Event.c b/libparc/parc/algol/test/test_parc_Event.c new file mode 100644 index 00000000..261cd079 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_Event.c @@ -0,0 +1,243 @@ +/* + * 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 <LongBow/unit-test.h> + +#include <parc/algol/parc_SafeMemory.h> + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../parc_Event.c" + +LONGBOW_TEST_RUNNER(parc_Event) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_Event) +{ + parcEvent_EnableDebug(); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_Event) +{ + parcEvent_DisableDebug(); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parc_Event_Create_Destroy); + LONGBOW_RUN_TEST_CASE(Global, parc_Event_Start); + LONGBOW_RUN_TEST_CASE(Global, parc_Event_Stop); + LONGBOW_RUN_TEST_CASE(Global, parc_Event_Poll); + LONGBOW_RUN_TEST_CASE(Global, parc_Event_SetPriority); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +static int _test_event_called = 0; + +static void +_test_event(int fd, PARCEventType flags, void *data) +{ + _test_event_called++; +} + +LONGBOW_TEST_CASE(Global, parc_Event_Create_Destroy) +{ + int fds[2]; + int result = socketpair(AF_LOCAL, SOCK_DGRAM, 0, fds); + assertFalse(result, "Socketpair creation failed.\n"); + + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + PARCEvent *parcEvent = parcEvent_Create(parcEventScheduler, fds[0], PARCEventType_Write, _test_event, NULL); + assertNotNull(parcEvent, "parcEvent_Create returned a null reference"); + parcEvent_Destroy(&parcEvent); + parcEventScheduler_Destroy(&parcEventScheduler); + + close(fds[0]); + close(fds[1]); +} + +LONGBOW_TEST_CASE(Global, parc_Event_Start) +{ + int fds[2]; + int result = socketpair(AF_LOCAL, SOCK_DGRAM, 0, fds); + assertFalse(result, "Socketpair creation failed.\n"); + + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + PARCEvent *parcEvent = parcEvent_Create(parcEventScheduler, fds[0], PARCEventType_Read | PARCEventType_Write, _test_event, NULL); + assertNotNull(parcEvent, "parcEvent_Create returned a null reference"); + + _test_event_called = 0; + parcEvent_Start(parcEvent); + parcEventScheduler_Start(parcEventScheduler, PARCEventSchedulerDispatchType_Blocking); + assertTrue(_test_event_called == 1, "Event never called."); + + parcEvent_Destroy(&parcEvent); + parcEventScheduler_Destroy(&parcEventScheduler); + close(fds[0]); + close(fds[1]); +} + +static int _test_stop_event_called = 0; + +static void +_test_stop_event(int fd, PARCEventType flags, void *data) +{ + PARCEvent **parcEvent = (PARCEvent **) data; + _test_stop_event_called++; + parcEvent_Stop(*parcEvent); +} + +LONGBOW_TEST_CASE(Global, parc_Event_Stop) +{ + int fds[2]; + int result = socketpair(AF_LOCAL, SOCK_DGRAM, 0, fds); + assertFalse(result, "Socketpair creation failed.\n"); + + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + PARCEvent *parcEvent = parcEvent_Create(parcEventScheduler, fds[0], PARCEventType_Write | PARCEventType_Persist, _test_stop_event, &parcEvent); + assertNotNull(parcEvent, "parcEvent_Create returned a null reference"); + + parcEvent_Start(parcEvent); + parcEventScheduler_Start(parcEventScheduler, PARCEventSchedulerDispatchType_Blocking); + assertTrue(_test_stop_event_called > 0, "Event never called."); + assertFalse(_test_stop_event_called != 1, "Event called more than once."); + + parcEvent_Destroy(&parcEvent); + parcEventScheduler_Destroy(&parcEventScheduler); + close(fds[0]); + close(fds[1]); +} + +LONGBOW_TEST_CASE(Global, parc_Event_Poll) +{ + int fds[2]; + int result = socketpair(AF_LOCAL, SOCK_DGRAM, 0, fds); + assertFalse(result, "Socketpair creation failed.\n"); + + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + PARCEvent *parcEvent = parcEvent_Create(parcEventScheduler, fds[0], PARCEventType_Write, _test_event, NULL); + assertNotNull(parcEvent, "parcEvent_Create returned a null reference"); + + result = parcEvent_Poll(parcEvent, PARCEventType_Read); + // should be no outstanding events + assertTrue(result == 0, "parcEvent_Poll returned %d\n", result); + + parcEvent_Destroy(&parcEvent); + parcEventScheduler_Destroy(&parcEventScheduler); + close(fds[0]); + close(fds[1]); +} + +static int _test_writeMaxPriority_event_called = 0; + +static void +_test_writeMaxPriority_event(int fd, PARCEventType flags, void *data) +{ + PARCEvent *parcEvent = *((PARCEvent **) data); + parcEvent_Stop(parcEvent); + _test_writeMaxPriority_event_called++; +} + +static int _test_writeMinPriority_event_called = 0; + +static void +_test_writeMinPriority_event(int fd, PARCEventType flags, void *data) +{ + PARCEvent *parcEvent = *((PARCEvent **) data); + parcEvent_Stop(parcEvent); + _test_writeMinPriority_event_called++; +} + +LONGBOW_TEST_CASE(Global, parc_Event_SetPriority) +{ + int fds[2]; + int result = socketpair(AF_LOCAL, SOCK_DGRAM, 0, fds); + assertFalse(result, "Socketpair creation failed.\n"); + + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + + // + // First event to be called back disables its partner's event + // + PARCEvent *parcEventWriteMax, *parcEventWriteMin; + parcEventWriteMax = parcEvent_Create(parcEventScheduler, fds[0], + PARCEventType_Write, + _test_writeMaxPriority_event, + (void *) &parcEventWriteMin); + assertNotNull(parcEventWriteMax, "parcEvent_Create returned a null reference"); + parcEventWriteMin = parcEvent_Create(parcEventScheduler, fds[1], + PARCEventType_Write, + _test_writeMinPriority_event, + (void *) &parcEventWriteMax); + assertNotNull(parcEventWriteMin, "parcEvent_Create returned a null reference"); + + result = parcEvent_SetPriority(parcEventWriteMin, PARCEventPriority_Minimum); + assertTrue(result == 0, "parcEvent_SetPriority write returned %d\n", result); + result = parcEvent_SetPriority(parcEventWriteMax, PARCEventPriority_Maximum); + assertTrue(result == 0, "parcEvent_SetPriority read returned %d\n", result); + + parcEvent_Start(parcEventWriteMin); + parcEvent_Start(parcEventWriteMax); + + parcEventScheduler_Start(parcEventScheduler, PARCEventSchedulerDispatchType_NonBlocking); + + assertTrue(_test_writeMaxPriority_event_called == 1, "Read event called before priority write event handled"); + assertTrue(_test_writeMinPriority_event_called == 0, "Write event never triggered"); + + parcEvent_Destroy(&parcEventWriteMax); + parcEvent_Destroy(&parcEventWriteMin); + parcEventScheduler_Destroy(&parcEventScheduler); + close(fds[0]); + close(fds[1]); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_Event); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_EventBuffer.c b/libparc/parc/algol/test/test_parc_EventBuffer.c new file mode 100644 index 00000000..c0b8d8c9 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_EventBuffer.c @@ -0,0 +1,345 @@ +/* + * 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 <stdio.h> +#include <pthread.h> + +#include <arpa/inet.h> + +#include <LongBow/unit-test.h> + +#include <parc/algol/parc_SafeMemory.h> +#include <parc/algol/parc_EventBuffer.h> + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../parc_EventBuffer.c" + +LONGBOW_TEST_RUNNER(parc_EventBuffer) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_EventBuffer) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_EventBuffer) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parc_EventBuffer_Create_Destroy); + LONGBOW_RUN_TEST_CASE(Global, parc_EventBuffer_GetLength_Append); + LONGBOW_RUN_TEST_CASE(Global, parc_EventBuffer_Prepend_Pullup); + LONGBOW_RUN_TEST_CASE(Global, parc_EventBuffer_ReadIntoBuffer); + LONGBOW_RUN_TEST_CASE(Global, parc_EventBuffer_AppendBuffer); + LONGBOW_RUN_TEST_CASE(Global, parc_EventBuffer_Read); + LONGBOW_RUN_TEST_CASE(Global, parc_EventBuffer_WriteToFileDescriptor); + LONGBOW_RUN_TEST_CASE(Global, parc_EventBuffer_ReadFromFileDescriptor); + LONGBOW_RUN_TEST_CASE(Global, parc_EventBuffer_ReadLine_FreeLine); + LONGBOW_RUN_TEST_CASE(Global, parc_EventBuffer_GetQueueBuffer); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parc_EventBuffer_Create_Destroy) +{ + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + + parcEventBuffer_EnableDebug(parcEventScheduler_GetLogger(parcEventScheduler)); + + PARCEventBuffer *parcEventBuffer = parcEventBuffer_Create(); + assertNotNull(parcEventBuffer, "parcEventBuffer_Create returned a null reference"); + + parcEventBuffer_Destroy(&parcEventBuffer); + parcEventScheduler_Destroy(&parcEventScheduler); + parcEventBuffer_DisableDebug(); +} + +static int _dataLength = 8192; + +LONGBOW_TEST_CASE(Global, parc_EventBuffer_GetLength_Append) +{ + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + + parcEventBuffer_EnableDebug(parcEventScheduler_GetLogger(parcEventScheduler)); + + PARCEventBuffer *parcEventBuffer = parcEventBuffer_Create(); + assertNotNull(parcEventBuffer, "parcEventBuffer_Create returned a null reference"); + + char data[_dataLength]; + parcEventBuffer_Append(parcEventBuffer, data, _dataLength); + assertTrue(parcEventBuffer_GetLength(parcEventBuffer) == _dataLength, "Buffer length does not match length of appended data"); + + parcEventBuffer_Destroy(&parcEventBuffer); + parcEventScheduler_Destroy(&parcEventScheduler); +} + +static uint8_t prependedDataValue = '1'; + +LONGBOW_TEST_CASE(Global, parc_EventBuffer_Prepend_Pullup) +{ + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + + parcEventBuffer_EnableDebug(parcEventScheduler_GetLogger(parcEventScheduler)); + + PARCEventBuffer *parcEventBuffer = parcEventBuffer_Create(); + assertNotNull(parcEventBuffer, "parcEventBuffer_Create returned a null reference"); + + char data[_dataLength]; + data[0] = 2; + parcEventBuffer_Append(parcEventBuffer, data, _dataLength); + assertTrue(parcEventBuffer_GetLength(parcEventBuffer) == _dataLength, "Buffer length does not match length of appended data"); + + uint8_t prependedData[1]; + prependedData[0] = prependedDataValue; + parcEventBuffer_Prepend(parcEventBuffer, prependedData, sizeof(uint8_t)); + size_t bufferSize = parcEventBuffer_GetLength(parcEventBuffer); + assertTrue(bufferSize == (_dataLength + 1), "Buffer length does not match length plus prepended data length"); + + uint8_t *completeBuffer = parcEventBuffer_Pullup(parcEventBuffer, -1); + assertTrue(completeBuffer[0] == prependedDataValue, "Prepended data doesn't match %d != %d", completeBuffer[0], prependedDataValue); + assertTrue(completeBuffer[1] == 2, "Consolidated data doesn't match %d != %d", completeBuffer[1], 2); + + parcEventBuffer_Destroy(&parcEventBuffer); + parcEventScheduler_Destroy(&parcEventScheduler); +} + +LONGBOW_TEST_CASE(Global, parc_EventBuffer_ReadIntoBuffer) +{ + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + + parcEventBuffer_EnableDebug(parcEventScheduler_GetLogger(parcEventScheduler)); + + PARCEventBuffer *parcEventBufferSource = parcEventBuffer_Create(); + assertNotNull(parcEventBufferSource, "parcEventBuffer_Create returned a null reference"); + char sourceData[_dataLength]; + parcEventBuffer_Append(parcEventBufferSource, sourceData, _dataLength); + + PARCEventBuffer *parcEventBufferDestination = parcEventBuffer_Create(); + assertNotNull(parcEventBufferDestination, "parcEventBuffer_Create returned a null reference"); + char destinationData[_dataLength]; + parcEventBuffer_Append(parcEventBufferDestination, destinationData, _dataLength); + + parcEventBuffer_ReadIntoBuffer(parcEventBufferSource, parcEventBufferDestination, -1); + size_t bufferSize = parcEventBuffer_GetLength(parcEventBufferDestination); + assertTrue(bufferSize == (_dataLength * 2), "Destination buffer size doesn't match expected length"); + + parcEventBuffer_Destroy(&parcEventBufferSource); + parcEventBuffer_Destroy(&parcEventBufferDestination); + parcEventScheduler_Destroy(&parcEventScheduler); +} + +LONGBOW_TEST_CASE(Global, parc_EventBuffer_AppendBuffer) +{ + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + + parcEventBuffer_EnableDebug(parcEventScheduler_GetLogger(parcEventScheduler)); + + PARCEventBuffer *parcEventBufferSource = parcEventBuffer_Create(); + assertNotNull(parcEventBufferSource, "parcEventBuffer_Create returned a null reference"); + char sourceData[_dataLength]; + parcEventBuffer_Append(parcEventBufferSource, sourceData, _dataLength); + + PARCEventBuffer *parcEventBufferDestination = parcEventBuffer_Create(); + assertNotNull(parcEventBufferDestination, "parcEventBuffer_Create returned a null reference"); + char destinationData[_dataLength]; + parcEventBuffer_Append(parcEventBufferDestination, destinationData, _dataLength); + + parcEventBuffer_AppendBuffer(parcEventBufferSource, parcEventBufferDestination); + size_t bufferSize = parcEventBuffer_GetLength(parcEventBufferDestination); + assertTrue(bufferSize == (_dataLength * 2), "Destination buffer size doesn't match expected length, %zu != %d", bufferSize, _dataLength * 2); + + parcEventBuffer_Destroy(&parcEventBufferSource); + parcEventBuffer_Destroy(&parcEventBufferDestination); + parcEventScheduler_Destroy(&parcEventScheduler); +} + +LONGBOW_TEST_CASE(Global, parc_EventBuffer_Read) +{ + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + + parcEventBuffer_EnableDebug(parcEventScheduler_GetLogger(parcEventScheduler)); + + PARCEventBuffer *parcEventBuffer = parcEventBuffer_Create(); + assertNotNull(parcEventBuffer, "parcEventBuffer_Create returned a null reference"); + + char sourceData[64] = "This is a test"; + parcEventBuffer_Append(parcEventBuffer, sourceData, 64); + + char readDataBuffer[64]; + int length = parcEventBuffer_Read(parcEventBuffer, readDataBuffer, 32); + + assertTrue(strncmp(sourceData, readDataBuffer, 32) == 0, + "Buffer contents written do not match contents read"); + assertTrue(length == 32, "parcEventBuffer_Read length unexpected %d != 32\n", length); + + length = parcEventBuffer_Read(parcEventBuffer, NULL, 64); + assertTrue(length == 0, "Drain of parcEventBuffer returned %d", length); + + parcEventBuffer_Destroy(&parcEventBuffer); + parcEventScheduler_Destroy(&parcEventScheduler); +} + +LONGBOW_TEST_CASE(Global, parc_EventBuffer_WriteToFileDescriptor) +{ + int fds[2]; + int result = socketpair(AF_LOCAL, SOCK_DGRAM, 0, fds); + assertFalse(result, "Socketpair creation failed.\n"); + + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + + parcEventBuffer_EnableDebug(parcEventScheduler_GetLogger(parcEventScheduler)); + + PARCEventBuffer *parcEventBuffer = parcEventBuffer_Create(); + assertNotNull(parcEventBuffer, "parcEventBuffer_Create returned a null reference"); + + char sourceData[100] = "This is a test"; + parcEventBuffer_Append(parcEventBuffer, sourceData, 64); + size_t written = parcEventBuffer_WriteToFileDescriptor(parcEventBuffer, fds[0], 64); + + assertTrue(written == 64, "Length written does not match buffer length."); + assertTrue(read(fds[1], sourceData, 100) == 64, "Length read does not match length written."); + + parcEventBuffer_Destroy(&parcEventBuffer); + parcEventScheduler_Destroy(&parcEventScheduler); + close(fds[0]); + close(fds[1]); +} + +LONGBOW_TEST_CASE(Global, parc_EventBuffer_ReadFromFileDescriptor) +{ + int fds[2]; + int result = socketpair(AF_LOCAL, SOCK_DGRAM, 0, fds); + assertFalse(result, "Socketpair creation failed.\n"); + + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + + parcEventBuffer_EnableDebug(parcEventScheduler_GetLogger(parcEventScheduler)); + + PARCEventBuffer *parcEventBuffer = parcEventBuffer_Create(); + assertNotNull(parcEventBuffer, "parcEventBuffer_Create returned a null reference"); + + char sourceData[64] = "This is a test"; + parcEventBuffer_Append(parcEventBuffer, sourceData, 64); + + size_t written = parcEventBuffer_WriteToFileDescriptor(parcEventBuffer, fds[0], 64); + assertTrue(written == 64, "Length written does not match buffer length."); + size_t read = parcEventBuffer_ReadFromFileDescriptor(parcEventBuffer, fds[1], -1); + + assertTrue(read == 64, "Length read does not match amount written."); + + parcEventBuffer_Destroy(&parcEventBuffer); + parcEventScheduler_Destroy(&parcEventScheduler); + close(fds[0]); + close(fds[1]); +} + +LONGBOW_TEST_CASE(Global, parc_EventBuffer_ReadLine_FreeLine) +{ + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + + parcEventBuffer_EnableDebug(parcEventScheduler_GetLogger(parcEventScheduler)); + + PARCEventBuffer *parcEventBuffer = parcEventBuffer_Create(); + assertNotNull(parcEventBuffer, "parcEventBuffer_Create returned a null reference"); + + char sourceData[64] = "This is a test\n"; + parcEventBuffer_Append(parcEventBuffer, sourceData, 64); + assertTrue(parcEventBuffer_GetLength(parcEventBuffer) == 64, "parcEventBuffer has wrong length %zu.", parcEventBuffer_GetLength(parcEventBuffer)); + + size_t bytesRead; + char *lineRead = parcEventBuffer_ReadLine(parcEventBuffer, &bytesRead); + // read up to newline and terminate, so we read the newline but don't return it in the result + assertTrue(parcEventBuffer_GetLength(parcEventBuffer) == (64 - (bytesRead + 1)), + "parcEventBuffer has wrong length %zu != %zu.", + (64 - (bytesRead + 1)), parcEventBuffer_GetLength(parcEventBuffer)); + assertTrue(strncmp(sourceData, lineRead, bytesRead) == 0, "Line read doesn't match %s != %s", sourceData, lineRead); + assertTrue((strlen(sourceData) - strlen(lineRead)) == 1, "Line length doesn't match %zu != %zu", strlen(sourceData), strlen(lineRead)); + + parcEventBuffer_FreeLine(parcEventBuffer, &lineRead); + + parcEventBuffer_Destroy(&parcEventBuffer); + parcEventScheduler_Destroy(&parcEventScheduler); +} + +LONGBOW_TEST_CASE(Global, parc_EventBuffer_GetQueueBuffer) +{ + int fds[2]; + int result = socketpair(AF_LOCAL, SOCK_DGRAM, 0, fds); + assertFalse(result, "Socketpair creation failed.\n"); + + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + + parcEventBuffer_EnableDebug(parcEventScheduler_GetLogger(parcEventScheduler)); + + PARCEventQueue *parcEventQueue = parcEventQueue_Create(parcEventScheduler, fds[0], 0); + assertNotNull(parcEventQueue, "parcEventQueue_Create returned a null reference"); + + PARCEventBuffer *parcEventBuffer_Output = parcEventBuffer_GetQueueBufferOutput(parcEventQueue); + assertNotNull(parcEventBuffer_Output, "Received null output buffer from queue"); + + PARCEventBuffer *parcEventBuffer_Input = parcEventBuffer_GetQueueBufferInput(parcEventQueue); + assertNotNull(parcEventBuffer_Input, "Received null input buffer from queue"); + + parcEventBuffer_Destroy(&parcEventBuffer_Output); + parcEventBuffer_Destroy(&parcEventBuffer_Input); + parcEventQueue_Destroy(&parcEventQueue); + parcEventScheduler_Destroy(&parcEventScheduler); + close(fds[0]); + close(fds[1]); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_EventBuffer); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_EventQueue.c b/libparc/parc/algol/test/test_parc_EventQueue.c new file mode 100644 index 00000000..862cb61d --- /dev/null +++ b/libparc/parc/algol/test/test_parc_EventQueue.c @@ -0,0 +1,402 @@ +/* + * 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 <stdio.h> +#include <pthread.h> + +#include <arpa/inet.h> + +#include <LongBow/unit-test.h> + +#include <parc/algol/parc_SafeMemory.h> +#include <parc/algol/parc_EventQueue.h> + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../parc_EventQueue.c" + +LONGBOW_TEST_RUNNER(parc_EventQueue) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_EventQueue) +{ + parcEventQueue_EnableDebug(); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_EventQueue) +{ + parcEventQueue_DisableDebug(); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parc_EventQueue_Create_Destroy); + LONGBOW_RUN_TEST_CASE(Global, parc_EventQueue_SetFileDescriptor_GetFileDecriptor); + LONGBOW_RUN_TEST_CASE(Global, parc_EventQueue_Get_Enable_Disable); + LONGBOW_RUN_TEST_CASE(Global, parc_EventQueue_SetCallbacks); + LONGBOW_RUN_TEST_CASE(Global, parc_EventQueue_Flush); + LONGBOW_RUN_TEST_CASE(Global, parc_EventQueue_Finished); + LONGBOW_RUN_TEST_CASE(Global, parc_EventQueue_SetWatermark); + LONGBOW_RUN_TEST_CASE(Global, parc_EventQueue_ReadWrite); + LONGBOW_RUN_TEST_CASE(Global, parc_EventQueue_SetPriority); + LONGBOW_RUN_TEST_CASE(Global, parc_EventQueue_Printf); + LONGBOW_RUN_TEST_CASE(Global, parc_EventQueue_GetEvBuffer); + LONGBOW_RUN_TEST_CASE(Global, parc_EventQueue_ConnectSocket); + LONGBOW_RUN_TEST_CASE(Global, parc_EventQueue_Create_Destroy_Pair); + LONGBOW_RUN_TEST_CASE(Global, parc_EventQueue_GetUpDownQueue); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parc_EventQueue_Create_Destroy) +{ + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + + PARCEventQueue *parcEventQueue = parcEventQueue_Create(parcEventScheduler, 0, 0); + assertNotNull(parcEventQueue, "parcEventQueue_Create returned a null reference"); + + parcEventQueue_Destroy(&parcEventQueue); + assertNull(parcEventQueue, "parcEventQueue_Destroy did not clear reference"); + parcEventScheduler_Destroy(&parcEventScheduler); + assertNull(parcEventScheduler, "parcEventScheduler_Destroy did not clear reference"); +} + +LONGBOW_TEST_CASE(Global, parc_EventQueue_Get_Enable_Disable) +{ + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + + PARCEventQueue *parcEventQueue = parcEventQueue_Create(parcEventScheduler, 0, 0); + assertNotNull(parcEventQueue, "parcEventQueue_Create returned a null reference"); + + PARCEventType defaultEvents = parcEventQueue_GetEnabled(parcEventQueue); + + parcEventQueue_Enable(parcEventQueue, PARCEventType_Read); + PARCEventType newEvents = parcEventQueue_GetEnabled(parcEventQueue); + assertTrue(newEvents == (defaultEvents | PARCEventType_Read), + "parcEventQueue_GetEnabled returned incorrect event set 0x%x != 0x%x", + newEvents, defaultEvents | PARCEventType_Read); + + parcEventQueue_Disable(parcEventQueue, PARCEventType_Read); + + newEvents = parcEventQueue_GetEnabled(parcEventQueue); + assertTrue(defaultEvents == newEvents, "parcEventQueue_GetEnabled returned incorrect event set 0x%x != 0x%x", newEvents, defaultEvents); + + parcEventQueue_Destroy(&parcEventQueue); + parcEventScheduler_Destroy(&parcEventScheduler); +} + +LONGBOW_TEST_CASE(Global, parc_EventQueue_SetFileDescriptor_GetFileDecriptor) +{ + int fds[2]; + int result = socketpair(AF_LOCAL, SOCK_DGRAM, 0, fds); + assertFalse(result, "Socketpair creation failed.\n"); + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + + PARCEventQueue *parcEventQueue = parcEventQueue_Create(parcEventScheduler, 0, 0); + assertNotNull(parcEventQueue, "parcEventQueue_Create returned null"); + + result = parcEventQueue_SetFileDescriptor(parcEventQueue, fds[0]); + assertTrue(result == 0, " parcEventQueue_SetFileDescriptor call failed"); + + result = parcEventQueue_GetFileDescriptor(parcEventQueue); + assertTrue(result == fds[0], "parcEventQueue_GetFileDescriptor failed"); + + parcEventQueue_Destroy(&parcEventQueue); + parcEventScheduler_Destroy(&parcEventScheduler); + close(fds[0]); + close(fds[1]); +} + +LONGBOW_TEST_CASE(Global, parc_EventQueue_Create_Destroy_Pair) +{ + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned null"); + + PARCEventQueuePair *parcEventQueuePair = parcEventQueue_CreateConnectedPair(parcEventScheduler); + assertNotNull(parcEventQueuePair, "parcEventQueue_CreateConnectedPair returned a null pair"); + + parcEventQueue_DestroyConnectedPair(&parcEventQueuePair); + parcEventScheduler_Destroy(&parcEventScheduler); +} + +LONGBOW_TEST_CASE(Global, parc_EventQueue_GetUpDownQueue) +{ + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + + PARCEventQueuePair *parcEventQueuePair = parcEventQueue_CreateConnectedPair(parcEventScheduler); + + assertNotNull(parcEventQueue_GetConnectedUpQueue(parcEventQueuePair), "parcEventQueue_GetUpQueue returned null"); + assertNotNull(parcEventQueue_GetConnectedDownQueue(parcEventQueuePair), "parcEventQueue_GetDownQueue returned null"); + + parcEventQueue_DestroyConnectedPair(&parcEventQueuePair); + parcEventScheduler_Destroy(&parcEventScheduler); +} + +static int _queue_callback_count = 0; + +static void +_queue_callback(PARCEventQueue *event, PARCEventType type, void *data) +{ + _queue_callback_count++; +} + +static int _queue_event_callback_count = 0; + +static void +_queue_event_callback(PARCEventQueue *event, PARCEventQueueEventType type, void *data) +{ + _queue_event_callback_count++; +} + +LONGBOW_TEST_CASE(Global, parc_EventQueue_SetCallbacks) +{ + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + + PARCEventQueue *parcEventQueue = parcEventQueue_Create(parcEventScheduler, 0, 0); + assertNotNull(parcEventQueue, "parcEventQueue_Create returned a null reference"); + + parcEventQueue_SetCallbacks(parcEventQueue, + _queue_callback, + _queue_callback, + _queue_event_callback, + NULL); + + _parc_queue_read_callback(NULL, parcEventQueue); + assertTrue(_queue_callback_count == 1, "Callback count expected 1 got %d", _queue_callback_count); + + _parc_queue_write_callback(NULL, parcEventQueue); + assertTrue(_queue_callback_count == 2, "Callback count expected 2 got %d", _queue_callback_count); + + _parc_queue_event_callback(NULL, PARCEventQueueEventType_EOF, parcEventQueue); + assertTrue(_queue_event_callback_count == 1, "Callback event count expected 1 got %d", _queue_event_callback_count); + + parcEventQueue_Destroy(&parcEventQueue); + parcEventScheduler_Destroy(&parcEventScheduler); +} + +LONGBOW_TEST_CASE(Global, parc_EventQueue_Flush) +{ + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + + PARCEventQueue *parcEventQueue = parcEventQueue_Create(parcEventScheduler, 0, 0); + assertNotNull(parcEventQueue, "parcEventQueue_Create returned a null reference"); + + int result = parcEventQueue_Flush(parcEventQueue, PARCEventType_Read); + assertTrue(result == 0, "parcEventQueue_Flush failed with %d", result); + + parcEventQueue_Destroy(&parcEventQueue); + parcEventScheduler_Destroy(&parcEventScheduler); +} + +LONGBOW_TEST_CASE(Global, parc_EventQueue_Finished) +{ + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + + PARCEventQueue *parcEventQueue = parcEventQueue_Create(parcEventScheduler, 0, 0); + assertNotNull(parcEventQueue, "parcEventQueue_Create returned a null reference"); + + int result = parcEventQueue_Finished(parcEventQueue, PARCEventType_Read); + assertTrue(result == 0, "parcEventQueue_Finished failed with %d", result); + + parcEventQueue_Destroy(&parcEventQueue); + parcEventScheduler_Destroy(&parcEventScheduler); +} + +LONGBOW_TEST_CASE(Global, parc_EventQueue_SetWatermark) +{ + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + + PARCEventQueue *parcEventQueue = parcEventQueue_Create(parcEventScheduler, 0, 0); + assertNotNull(parcEventQueue, "parcEventQueue_Create returned a null reference"); + + parcEventQueue_SetWatermark(parcEventQueue, PARCEventType_Read, 0, 0); + + parcEventQueue_Destroy(&parcEventQueue); + parcEventScheduler_Destroy(&parcEventScheduler); +} + +LONGBOW_TEST_CASE(Global, parc_EventQueue_ReadWrite) +{ + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + + PARCEventQueue *parcEventQueue = parcEventQueue_Create(parcEventScheduler, 0, 0); + assertNotNull(parcEventQueue, "parcEventQueue_Create returned a null reference"); + + char *data = "Hello World\n"; + int length = strlen(data); + int result = parcEventQueue_Write(parcEventQueue, data, length); + assertTrue(result == 0, "parcEventQueue_Write failed."); + + char buffer[64]; + result = parcEventQueue_Read(parcEventQueue, buffer, 64); + assertTrue(result == 0, "parcEventQueue_Read failed."); + + parcEventQueue_Destroy(&parcEventQueue); + parcEventScheduler_Destroy(&parcEventScheduler); +} + +static int _test_writeMaxPriority_event_called = 0; +static void +_test_writeMaxPriority_callback(PARCEventQueue *parcEventQueue, PARCEventType event, void *data) +{ + PARCEventQueue *parcEventQueuePartner = *((PARCEventQueue **) data); + parcEventQueue_Disable(parcEventQueuePartner, event); + _test_writeMaxPriority_event_called++; +} + +static int _test_writeMinPriority_event_called = 0; +static void +_test_writeMinPriority_callback(PARCEventQueue *parcEventQueue, PARCEventType event, void *data) +{ + PARCEventQueue *parcEventQueuePartner = *((PARCEventQueue **) data); + parcEventQueue_Disable(parcEventQueuePartner, event); + _test_writeMinPriority_event_called++; +} + +LONGBOW_TEST_CASE(Global, parc_EventQueue_SetPriority) +{ + int fds[2]; + int result = socketpair(AF_LOCAL, SOCK_DGRAM, 0, fds); + assertFalse(result, "Socketpair creation failed.\n"); + + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + + // + // First queue to be called back disables its partner's queue + // + PARCEventQueue *parcEventQueueMin, *parcEventQueueMax; + parcEventQueueMin = parcEventQueue_Create(parcEventScheduler, fds[0], PARCEventQueueOption_CloseOnFree); + assertNotNull(parcEventQueueMin, "parcEventQueue_Create returned a null reference"); + parcEventQueue_SetCallbacks(parcEventQueueMin, NULL, _test_writeMinPriority_callback, NULL, (void *) &parcEventQueueMax); + + parcEventQueueMax = parcEventQueue_Create(parcEventScheduler, fds[0], PARCEventQueueOption_CloseOnFree); + assertNotNull(parcEventQueueMax, "parcEventQueue_Create returned a null reference"); + parcEventQueue_SetCallbacks(parcEventQueueMax, NULL, _test_writeMaxPriority_callback, NULL, (void *) &parcEventQueueMin); + + result = parcEventQueue_SetPriority(parcEventQueueMin, PARCEventPriority_Minimum); + assertTrue(result == 0, "parcEventQueue_SetPriority Minimum priority failed."); + result = parcEventQueue_SetPriority(parcEventQueueMax, PARCEventPriority_Maximum); + assertTrue(result == 0, "parcEventQueue_SetPriority Maximum priority failed."); + + parcEventQueue_Enable(parcEventQueueMin, PARCEventType_Write); + parcEventQueue_Enable(parcEventQueueMax, PARCEventType_Write); + + parcEventScheduler_Start(parcEventScheduler, PARCEventSchedulerDispatchType_NonBlocking); + + assertTrue(_test_writeMaxPriority_event_called == 1, "Read event called before priority write event handled"); + assertTrue(_test_writeMinPriority_event_called == 0, "Write event never triggered"); + + parcEventQueue_Destroy(&parcEventQueueMin); + parcEventQueue_Destroy(&parcEventQueueMax); + parcEventScheduler_Destroy(&parcEventScheduler); + close(fds[0]); + close(fds[1]); +} + +LONGBOW_TEST_CASE(Global, parc_EventQueue_Printf) +{ + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + + PARCEventQueue *parcEventQueue = parcEventQueue_Create(parcEventScheduler, 0, 0); + assertNotNull(parcEventQueue, "parcEventQueue_Create returned a null reference"); + + int result = parcEventQueue_Printf(parcEventQueue, "%s %s\n", "Hello", "World"); + assertTrue(result == 12, "parcEventQueue_Printf didn't write expected length %d != %d", result, 12); + + parcEventQueue_Destroy(&parcEventQueue); + parcEventScheduler_Destroy(&parcEventScheduler); +} + +LONGBOW_TEST_CASE(Global, parc_EventQueue_GetEvBuffer) +{ + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + + PARCEventQueue *parcEventQueue = parcEventQueue_Create(parcEventScheduler, 0, 0); + assertNotNull(parcEventQueue, "parcEventQueue_Create returned a null reference"); + + struct evbuffer *result = internal_parcEventQueue_GetEvInputBuffer(parcEventQueue); + assertTrue(result != NULL, "parcEventQueue_GetEvInputBuffer failed."); + + result = internal_parcEventQueue_GetEvOutputBuffer(parcEventQueue); + assertTrue(result != NULL, "parcEventQueue_GetEvOutputBuffer failed."); + + parcEventQueue_Destroy(&parcEventQueue); + parcEventScheduler_Destroy(&parcEventScheduler); +} + +LONGBOW_TEST_CASE(Global, parc_EventQueue_ConnectSocket) +{ + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + + PARCEventQueue *parcEventQueue = parcEventQueue_Create(parcEventScheduler, -1, 0); + assertNotNull(parcEventQueue, "parcEventQueue_Create returned a null reference"); + + struct sockaddr_in address; + int addressLength = sizeof(address); + memset(&address, 0, addressLength); + address.sin_family = AF_INET; + address.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */ + address.sin_port = htons(8080); /* Port 8080 */ + + int result = parcEventQueue_ConnectSocket(parcEventQueue, (struct sockaddr *) &address, addressLength); + assertTrue(result == 0, "parcEventQueue_ConnectSocket returned %d", result); + + parcEventQueue_Destroy(&parcEventQueue); + parcEventScheduler_Destroy(&parcEventScheduler); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_EventQueue); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_EventScheduler.c b/libparc/parc/algol/test/test_parc_EventScheduler.c new file mode 100755 index 00000000..8195bd2e --- /dev/null +++ b/libparc/parc/algol/test/test_parc_EventScheduler.c @@ -0,0 +1,263 @@ +/* + * 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 <stdio.h> + +#include <pthread.h> + +#include <LongBow/unit-test.h> + +#include "../internal_parc_Event.h" +#include <parc/algol/parc_SafeMemory.h> +#include <parc/algol/parc_EventScheduler.h> +#include <parc/algol/parc_EventTimer.h> + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../parc_EventScheduler.c" + +LONGBOW_TEST_RUNNER(parc_EventScheduler) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_EventScheduler) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_EventScheduler) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parc_EventScheduler_Create_Destroy); + LONGBOW_RUN_TEST_CASE(Global, parc_EventScheduler_Run); + LONGBOW_RUN_TEST_CASE(Global, parc_EventScheduler_Dispatch); + LONGBOW_RUN_TEST_CASE(Global, parc_EventScheduler_Stop); + LONGBOW_RUN_TEST_CASE(Global, parc_EventScheduler_Abort); + LONGBOW_RUN_TEST_CASE(Global, parc_EventScheduler_Memory); + LONGBOW_RUN_TEST_CASE(Global, parc_EventScheduler_GetEvBase); + LONGBOW_RUN_TEST_CASE(Global, parc_EventScheduler_GetLogger); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + parcEventScheduler_EnableDebug(); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + + parcEventScheduler_DisableDebug(); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parc_EventScheduler_Create_Destroy) +{ + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + parcEventScheduler_Destroy(&parcEventScheduler); + assertNull(parcEventScheduler, "parcEventScheduler_Destroy failed to null reference"); +} + +static void +_event_callback(int fd, PARCEventType flags, void *data) +{ + (*(unsigned *) data)++; +} + +static int _callback_event_called = 0; + +LONGBOW_TEST_CASE(Global, parc_EventScheduler_Run) +{ + _callback_event_called = 0; + + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + PARCEventTimer *parcEvent = parcEventTimer_Create(parcEventScheduler, 0, _event_callback, (void *) &_callback_event_called); + assertNotNull(parcEvent, "parcEventTimer_Create returned a null reference"); + + struct timeval fs = { 0, 1 }; + parcEventTimer_Start(parcEvent, &fs); + + parcEventScheduler_Start(parcEventScheduler, PARCEventSchedulerDispatchType_Blocking); + assertTrue(_callback_event_called == 1, "Timer event never called back"); + + parcEventTimer_Destroy(&parcEvent); + parcEventScheduler_Destroy(&parcEventScheduler); +} + +LONGBOW_TEST_CASE(Global, parc_EventScheduler_Dispatch) +{ + _callback_event_called = 0; + + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + PARCEventTimer *parcEvent = parcEventTimer_Create(parcEventScheduler, 0, _event_callback, (void *) &_callback_event_called); + assertNotNull(parcEvent, "parcEventTimer_Create returned a null reference"); + + struct timeval fs = { 0, 1 }; + parcEventTimer_Start(parcEvent, &fs); + + // This will block until the event is processed + parcEventScheduler_DispatchBlocking(parcEventScheduler); + assertTrue(_callback_event_called == 1, "Timer event never called back"); + + // Start the timer + struct timeval longerfs = { 1, 0 }; // 1s + parcEventTimer_Start(parcEvent, &longerfs); + parcEventScheduler_DispatchNonBlocking(parcEventScheduler); + assertTrue(_callback_event_called == 1, "Timer event called again prematurely"); + usleep(2000000); // 2s + parcEventScheduler_DispatchNonBlocking(parcEventScheduler); + assertTrue(_callback_event_called == 2, "Timer event never called back"); + + parcEventTimer_Destroy(&parcEvent); + parcEventScheduler_Destroy(&parcEventScheduler); +} + +static void +_stop_callback(int fd, PARCEventType flags, void *data) +{ + PARCEventScheduler *parcEventScheduler = (PARCEventScheduler *) data; + struct timeval fs = { 0, 0 }; + parcEventScheduler_Stop(parcEventScheduler, &fs); + _callback_event_called++; +} + +LONGBOW_TEST_CASE(Global, parc_EventScheduler_Stop) +{ + _callback_event_called = 0; + + // Create a new scheduler + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + + // Create a persistent event which will repeat until stopped + PARCEventTimer *parcEvent = parcEventTimer_Create(parcEventScheduler, PARCEventType_Persist, _stop_callback, parcEventScheduler); + assertNotNull(parcEvent, "parcEventTimer_Create returned a null reference"); + + struct timeval fs = { 1, 0 }; + parcEventTimer_Start(parcEvent, &fs); + + parcEventScheduler_Start(parcEventScheduler, PARCEventSchedulerDispatchType_Blocking); + assertTrue(_callback_event_called == 1, "Timer event never called back"); + + parcEventTimer_Destroy(&parcEvent); + parcEventScheduler_Destroy(&parcEventScheduler); +} + +static void +_abort_callback(int fd, PARCEventType flags, void *data) +{ + PARCEventScheduler *parcEventScheduler = (PARCEventScheduler *) data; + parcEventScheduler_Abort(parcEventScheduler); +} + +LONGBOW_TEST_CASE(Global, parc_EventScheduler_Abort) +{ + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + PARCEventTimer *parcEvent = parcEventTimer_Create(parcEventScheduler, PARCEventType_Persist, _abort_callback, parcEventScheduler); + assertNotNull(parcEvent, "parcEventTimer_Create returned a null reference"); + + struct timeval fs = { 1, 0 }; + parcEventTimer_Start(parcEvent, &fs); + + parcEventScheduler_Start(parcEventScheduler, PARCEventSchedulerDispatchType_Blocking); + + parcEventTimer_Destroy(&parcEvent); + parcEventScheduler_Destroy(&parcEventScheduler); +} + +static void +_test_memory_event(int fd, short flags, void *data) +{ +} + +/** + * Ensure that the scheduler is using parc memory inside libevent + */ +LONGBOW_TEST_CASE(Global, parc_EventScheduler_Memory) +{ + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + + size_t baseline = parcMemory_Outstanding(); + + struct event *testEvent = event_new(parcEventScheduler_GetEvBase(parcEventScheduler), -1, 0, _test_memory_event, NULL); + + assertTrue(parcMemory_Outstanding() > baseline, + "event_new() did not increase parcMemory_Outstanding: baseline %zu now %u", + baseline, + parcMemory_Outstanding()); + + event_free(testEvent); + + assertTrue(parcMemory_Outstanding() == baseline, + "event_free() did reduce to baseline: baseline %zu now %u", + baseline, + parcMemory_Outstanding()); + + parcEventScheduler_Destroy(&parcEventScheduler); + + assertTrue(parcSafeMemory_ReportAllocation(STDOUT_FILENO) == 0, "Memory imbalance on create/destroy: %u", parcMemory_Outstanding()); +} + +LONGBOW_TEST_CASE(Global, parc_EventScheduler_GetEvBase) +{ + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + + assertNotNull(parcEventScheduler_GetEvBase(parcEventScheduler), "Expected a non-null EV pointer."); + + parcEventScheduler_Destroy(&parcEventScheduler); +} + +LONGBOW_TEST_CASE(Global, parc_EventScheduler_GetLogger) +{ + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + + assertNotNull(parcEventScheduler_GetLogger(parcEventScheduler), "Expected a non-null logger."); + + parcEventScheduler_Destroy(&parcEventScheduler); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_EventScheduler); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_EventSignal.c b/libparc/parc/algol/test/test_parc_EventSignal.c new file mode 100644 index 00000000..fcb86333 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_EventSignal.c @@ -0,0 +1,167 @@ +/* + * 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 <stdio.h> +#include <pthread.h> + +#include <LongBow/unit-test.h> + +#include <parc/algol/parc_SafeMemory.h> +#include <parc/algol/parc_EventSignal.h> + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../parc_EventSignal.c" + +LONGBOW_TEST_RUNNER(parc_EventSignal) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_EventSignal) +{ + parcEventSignal_EnableDebug(); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_EventSignal) +{ + parcEventSignal_DisableDebug(); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parc_EventSignal_Create_Destroy); + LONGBOW_RUN_TEST_CASE(Global, parc_EventSignal_Start_Stop); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +static int _empty_event_called = 0; + +static void +_empty_event(int fd, PARCEventType flags, void *data) +{ + _empty_event_called++; +} + +LONGBOW_TEST_CASE(Global, parc_EventSignal_Create_Destroy) +{ + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + + PARCEventSignal *parcEventSignal = parcEventSignal_Create(parcEventScheduler, SIGUSR1, PARCEventType_Signal | PARCEventType_Persist, _empty_event, NULL); + assertNotNull(parcEventSignal, "parcEventSignal_Create returned a null reference"); + + _parc_event_signal_callback(0, 0, (void *) parcEventSignal); + assertTrue(_empty_event_called == 1, "Event handler never called."); + parcEventSignal_Destroy(&parcEventSignal); + parcEventScheduler_Destroy(&parcEventScheduler); +} + +#include <pthread.h> + +static void * +_run_scheduler(void *data) +{ + PARCEventScheduler *parcEventScheduler = (PARCEventScheduler *) data; + parcEventScheduler_Start(parcEventScheduler, PARCEventSchedulerDispatchType_Blocking); + return NULL; +} + +static int _test_event_called = 0; + +static void +_signal_event(int fd, PARCEventType flags, void *data) +{ + PARCEventSignal **parcEventSignal = (PARCEventSignal **) data; + _test_event_called++; + parcEventSignal_Stop(*parcEventSignal); +} + +LONGBOW_TEST_CASE(Global, parc_EventSignal_Start_Stop) +{ + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + PARCEventSignal *parcEventSignal = parcEventSignal_Create(parcEventScheduler, SIGUSR1, PARCEventType_Signal | PARCEventType_Persist, _signal_event, &parcEventSignal); + assertNotNull(parcEventSignal, "parcEventSignal_Create returned a null reference"); + + parcEventSignal_Start(parcEventSignal); + + pthread_t thread; + pthread_create(&thread, NULL, _run_scheduler, parcEventScheduler); + + kill(getpid(), SIGUSR1); + pthread_join(thread, NULL); + assertTrue(_test_event_called == 1, "Event never called."); + + parcEventSignal_Destroy(&parcEventSignal); + parcEventScheduler_Destroy(&parcEventScheduler); +} + +static void +_test_stop_event(int fd, PARCEventType flags, void *data) +{ + PARCEventSignal **parcEventSignal = (PARCEventSignal **) data; + _test_event_called++; + parcEventSignal_Stop(*parcEventSignal); +} + +LONGBOW_TEST_CASE(Global, parc_EventSignal_Stop) +{ + _test_event_called = 0; + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + PARCEventSignal *parcEventSignal = parcEventSignal_Create(parcEventScheduler, SIGUSR1, PARCEventType_Signal | PARCEventType_Persist, _test_stop_event, &parcEventSignal); + assertNotNull(parcEventSignal, "parcEventSignal_Create returned a null reference"); + + parcEventSignal_Start(parcEventSignal); + kill(getpid(), SIGUSR1); + + parcEventScheduler_Start(parcEventScheduler, PARCEventSchedulerDispatchType_Blocking); + assertTrue(_test_event_called == 1, "Event never called."); + + parcEventSignal_Destroy(&parcEventSignal); + parcEventScheduler_Destroy(&parcEventScheduler); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_EventSignal); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_EventSocket.c b/libparc/parc/algol/test/test_parc_EventSocket.c new file mode 100644 index 00000000..dbe8892b --- /dev/null +++ b/libparc/parc/algol/test/test_parc_EventSocket.c @@ -0,0 +1,128 @@ +/* + * 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 <stdio.h> +#include <pthread.h> + +#include <arpa/inet.h> + +#include <LongBow/unit-test.h> + +#include <parc/algol/parc_SafeMemory.h> +#include <parc/algol/parc_EventSocket.h> + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../parc_EventSocket.c" + +LONGBOW_TEST_RUNNER(parc_EventSocket) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_EventSocket) +{ + parcEventSocket_EnableDebug(); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_EventSocket) +{ + parcEventSocket_DisableDebug(); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parc_EventSocket_Create_Destroy); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +static int _test_event_called = 0; + +static void +listener_callback(int fd, struct sockaddr *sa, int socklen, void *user_data) +{ + _test_event_called++; +} + +static int _test_error_event_called = 0; + +static void +listener_error_callback(PARCEventScheduler *base, int error, char *errorString, void *addr_unix) +{ + _test_error_event_called++; +} + +LONGBOW_TEST_CASE(Global, parc_EventSocket_Create_Destroy) +{ + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(49009); + inet_pton(AF_INET, "127.0.0.1", &(addr.sin_addr)); + + PARCEventSocket *parcEventSocket = parcEventSocket_Create(parcEventScheduler, + listener_callback, + listener_error_callback, + NULL, NULL, 0); + assertNull(parcEventSocket, "parcEventSocket_Create didn't return an error when expected"); + + parcEventSocket = parcEventSocket_Create(parcEventScheduler, + listener_callback, + listener_error_callback, + NULL, (struct sockaddr *) &addr, sizeof(addr)); + assertNotNull(parcEventSocket, "parcEventSocket_Create returned a null reference"); + + _parc_evconn_callback(NULL, 0, (struct sockaddr *) &addr, sizeof(addr), parcEventSocket); + assertTrue(_test_event_called == 1, "Listener callback wasn't triggered"); + _parc_evconn_error_callback(NULL, parcEventSocket); + assertTrue(_test_error_event_called == 1, "Listener error callback wasn't triggered"); + + parcEventSocket_Destroy(&parcEventSocket); + parcEventScheduler_Destroy(&parcEventScheduler); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_EventSocket); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_EventTimer.c b/libparc/parc/algol/test/test_parc_EventTimer.c new file mode 100755 index 00000000..f6b9917e --- /dev/null +++ b/libparc/parc/algol/test/test_parc_EventTimer.c @@ -0,0 +1,143 @@ +/* + * 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 <stdio.h> +#include <pthread.h> + +#include <LongBow/unit-test.h> + +#include <parc/algol/parc_SafeMemory.h> +#include <parc/algol/parc_EventTimer.h> + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../parc_EventTimer.c" + +LONGBOW_TEST_RUNNER(parc_EventTimer) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_EventTimer) +{ + parcEventTimer_EnableDebug(); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_EventTimer) +{ + parcEventTimer_DisableDebug(); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parc_EventTimer_Create_Destroy); + LONGBOW_RUN_TEST_CASE(Global, parc_EventTimer_Start); + LONGBOW_RUN_TEST_CASE(Global, parc_EventTimer_Stop); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +static int _test_event_called = 0; + +static void +_test_event(int fd, PARCEventType flags, void *data) +{ + _test_event_called++; +} + +LONGBOW_TEST_CASE(Global, parc_EventTimer_Create_Destroy) +{ + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + + PARCEventTimer *parcEventTimer = parcEventTimer_Create(parcEventScheduler, PARCEventType_None, _test_event, NULL); + assertNotNull(parcEventTimer, "parcEventTimer_Create returned a null reference"); + + parcEventTimer_Destroy(&parcEventTimer); + parcEventScheduler_Destroy(&parcEventScheduler); +} + +LONGBOW_TEST_CASE(Global, parc_EventTimer_Start) +{ + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + + PARCEventTimer *parcEventTimer = parcEventTimer_Create(parcEventScheduler, PARCEventType_None, _test_event, NULL); + assertNotNull(parcEventTimer, "parcEventTimer_Create returned a null reference"); + + struct timeval timeout = { 1, 0 }; + parcEventTimer_Start(parcEventTimer, &timeout); + parcEventScheduler_Start(parcEventScheduler, PARCEventSchedulerDispatchType_Blocking); + assertTrue(_test_event_called == 1, "Event never called."); + + parcEventTimer_Destroy(&parcEventTimer); + parcEventScheduler_Destroy(&parcEventScheduler); +} + +static void +_test_stop_event(int fd, PARCEventType flags, void *data) +{ + PARCEventTimer **parcEventTimer = (PARCEventTimer **) data; + _test_event_called++; + parcEventTimer_Stop(*parcEventTimer); +} + +LONGBOW_TEST_CASE(Global, parc_EventTimer_Stop) +{ + PARCEventScheduler *parcEventScheduler = parcEventScheduler_Create(); + assertNotNull(parcEventScheduler, "parcEventScheduler_Create returned a null reference"); + + PARCEventTimer *parcEventTimer = parcEventTimer_Create(parcEventScheduler, PARCEventType_None | PARCEventType_Persist, _test_stop_event, &parcEventTimer); + assertNotNull(parcEventTimer, "parcEventTimer_Create returned a null reference"); + + _test_event_called = 0; + struct timeval timeout = { 1, 0 }; + parcEventTimer_Start(parcEventTimer, &timeout); + parcEventScheduler_Start(parcEventScheduler, PARCEventSchedulerDispatchType_Blocking); + assertTrue(_test_event_called == 1, "Event never called."); + + parcEventTimer_Destroy(&parcEventTimer); + parcEventScheduler_Destroy(&parcEventScheduler); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_EventTimer); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_File.c b/libparc/parc/algol/test/test_parc_File.c new file mode 100644 index 00000000..eccef210 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_File.c @@ -0,0 +1,208 @@ +/* + * 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 <LongBow/unit-test.h> +#include <fcntl.h> + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../parc_SafeMemory.h" +#include "../parc_File.c" + +#define PATH_SEGMENT "A" + +LONGBOW_TEST_RUNNER(parc_File) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Local); + LONGBOW_RUN_TEST_FIXTURE(AcquireRelease); + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_File) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_File) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(AcquireRelease) +{ + LONGBOW_RUN_TEST_CASE(AcquireRelease, parcFile_AcquireRelease); +} + +LONGBOW_TEST_FIXTURE_SETUP(AcquireRelease) +{ + longBowClipBoard_SetInt(testClipBoard, "initalAllocations", parcMemory_Outstanding()); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(AcquireRelease) +{ + uint64_t initialAllocations = longBowClipBoard_GetAsInt(testClipBoard, "initalAllocations"); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (initialAllocations < outstandingAllocations) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(AcquireRelease, parcFile_AcquireRelease) +{ + char *root = "/tmp/test_parc_File"; + + PARCFile *file = parcFile_Create(root); + + PARCFile *reference = parcFile_Acquire(file); + + parcFile_Release(&reference); + + parcFile_AssertValid(file); + + parcFile_Release(&file); +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcFile_CreateChild); + LONGBOW_RUN_TEST_CASE(Global, parcFile_CreateDeleteNewFile); + LONGBOW_RUN_TEST_CASE(Global, parcFile_CreateDelete_Directory); + LONGBOW_RUN_TEST_CASE(Global, parcFile_Exists); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + longBowClipBoard_SetInt(testClipBoard, "initalAllocations", parcMemory_Outstanding()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint64_t initialAllocations = longBowClipBoard_GetAsInt(testClipBoard, "initalAllocations"); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (initialAllocations < outstandingAllocations) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcFile_Exists) +{ + char *root = "/tmp"; + + PARCFile *parent = parcFile_Create(root); + + char *child = "foo"; + PARCFile *file = parcFile_CreateChild(parent, child); + + parcFile_CreateNewFile(file); + + bool actual = parcFile_Exists(file); + + assertTrue(actual, "Expected the file to exist."); + + parcFile_Release(&file); + parcFile_Release(&parent); +} + +LONGBOW_TEST_CASE(Global, parcFile_CreateChild) +{ + char *root = "/tmp"; + + PARCFile *parent = parcFile_Create(root); + + char *child = "foo"; + PARCFile *file = parcFile_CreateChild(parent, child); + + char *actual = parcFile_ToString(file); + + assertTrue(strcmp("/tmp/foo", actual) == 0, + "Expected %s, actual %s", "/tmp/foo", actual); + + parcMemory_Deallocate((void **) &actual); + parcFile_Release(&file); + parcFile_Release(&parent); +} + +LONGBOW_TEST_CASE(Global, parcFile_CreateDeleteNewFile) +{ + char *name = "/tmp/test_parc_File"; + + PARCFile *file = parcFile_Create(name); + + parcFile_CreateNewFile(file); + + bool actual = parcFile_Delete(file); + assertTrue(actual, "Expected parcFile_Delete to return true."); + + parcFile_Release(&file); +} + +LONGBOW_TEST_CASE(Global, parcFile_CreateDelete_Directory) +{ + char *name = "/tmp/test_parc_File_directory"; + + PARCFile *directory = parcFile_Create(name); + + parcFile_Mkdir(directory); + + char *fileName = "foo"; + PARCFile *file = parcFile_CreateChild(directory, fileName); + + bool success = parcFile_CreateNewFile(file); + assertTrue(success, "Expected parcFile_CreateNewFile success"); + + bool actual = parcFile_Delete(directory); + assertTrue(actual, "Expected parcFile_Delete to return true."); + + parcFile_Release(&file); + parcFile_Release(&directory); +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_File); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_FileChunker.c b/libparc/parc/algol/test/test_parc_FileChunker.c new file mode 100755 index 00000000..6060cff6 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_FileChunker.c @@ -0,0 +1,431 @@ +/* + * 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 the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../parc_FileChunker.c" + +#include <LongBow/unit-test.h> +#include <parc/algol/parc_SafeMemory.h> +#include <parc/algol/parc_BufferChunker.h> + +LONGBOW_TEST_RUNNER(parc_FileChunker) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_FileChunker) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_FileChunker) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parc_Chunker_CreateFromFile); + LONGBOW_RUN_TEST_CASE(Global, parc_Chunker_ForwardIterator_File); + LONGBOW_RUN_TEST_CASE(Global, parc_Chunker_ForwardIterator_FilePartial); + LONGBOW_RUN_TEST_CASE(Global, parc_Chunker_ForwardIterator_FileSmall); + LONGBOW_RUN_TEST_CASE(Global, parc_Chunker_ReverseIterator_File); + LONGBOW_RUN_TEST_CASE(Global, parc_Chunker_ReverseIterator_FilePartial); + LONGBOW_RUN_TEST_CASE(Global, parc_Chunker_ReverseIterator_FileSmall); + LONGBOW_RUN_TEST_CASE(Global, parc_Chunker_GetChunkSize); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +static void +_createFile(char *fname, PARCBuffer *buffer) +{ + PARCFile *file = parcFile_Create(fname); + if (!parcFile_Exists(file)) { + parcFile_CreateNewFile(file); + } + PARCRandomAccessFile *fhandle = parcRandomAccessFile_Open(file); + parcFile_Release(&file); + + parcRandomAccessFile_Write(fhandle, buffer); + parcRandomAccessFile_Close(fhandle); + parcRandomAccessFile_Release(&fhandle); +} + +static void +_deleteFile(char *fname) +{ + PARCFile *file = parcFile_Create(fname); + parcFile_Delete(file); + parcFile_Release(&file); +} + +LONGBOW_TEST_CASE(Global, parc_Chunker_CreateFromFile) +{ + PARCBuffer *buffer = parcBuffer_Allocate(1024); + + for (size_t i = 0; i < 32; i++) { + for (size_t j = 0; j < 32; j++) { + parcBuffer_PutUint8(buffer, i); + } + } + parcBuffer_Flip(buffer); + + _createFile("/tmp/file_chunker.tmp", buffer); + + PARCFile *file = parcFile_Create("/tmp/file_chunker.tmp"); + + PARCFileChunker *chunker = parcFileChunker_Create(file, 32); // each chunk is 32 bytes + PARCFileChunker *copy = parcFileChunker_Acquire(chunker); + assertNotNull(chunker, "Expected non-NULL Chunker"); + assertNotNull(copy, "Expected non-NULL copy of Chunker"); + parcFileChunker_Release(©); + + parcFile_Release(&file); + parcFileChunker_Release(&chunker); + + _deleteFile("/tmp/file_chunker.tmp"); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parc_Chunker_ForwardIterator_File) +{ + PARCBuffer *buffer = parcBuffer_Allocate(1024); + + for (size_t i = 0; i < 32; i++) { + for (size_t j = 0; j < 32; j++) { + parcBuffer_PutUint8(buffer, i); + } + } + parcBuffer_Flip(buffer); + + _createFile("/tmp/file_chunker.tmp", buffer); + + PARCFile *file = parcFile_Create("/tmp/file_chunker.tmp"); + + PARCFileChunker *chunker = parcFileChunker_Create(file, 32); // each chunk is 32 bytes + assertNotNull(chunker, "Expected non-NULL Chunker"); + + PARCIterator *itr = parcFileChunker_ForwardIterator(chunker); + size_t count = 0; + while (parcIterator_HasNext(itr)) { + PARCBuffer *payload = (PARCBuffer *) parcIterator_Next(itr); + + uint8_t *contents = parcBuffer_Overlay(payload, 0); + for (size_t i = 0; i < 32; i++) { + assertTrue(contents[i] == count, "Expected %zu at index %zu, got %d", count, i, contents[i]); + } + count++; + + parcBuffer_Release(&payload); + } + assertTrue(count == 32, "Expected to iterate over 32 content objects from the chunker, but for %zu", count); + parcIterator_Release(&itr); + + parcFile_Release(&file); + parcFileChunker_Release(&chunker); + + _deleteFile("/tmp/file_chunker.tmp"); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parc_Chunker_ForwardIterator_FilePartial) +{ + PARCBuffer *buffer = parcBuffer_Allocate(1030); + + for (size_t i = 0; i < 32; i++) { + for (size_t j = 0; j < 32; j++) { + parcBuffer_PutUint8(buffer, i); + } + } + + // Special 0xFF to mark the end... + for (int i = 0; i < 6; i++) { + parcBuffer_PutUint8(buffer, 0xFF); + } + parcBuffer_Flip(buffer); + + _createFile("/tmp/file_chunker.tmp", buffer); + + PARCFile *file = parcFile_Create("/tmp/file_chunker.tmp"); + + PARCFileChunker *chunker = parcFileChunker_Create(file, 32); // each chunk is 32 bytes + assertNotNull(chunker, "Expected non-NULL Chunker"); + + PARCIterator *itr = parcFileChunker_ForwardIterator(chunker); + size_t count = 0; + while (parcIterator_HasNext(itr)) { + PARCBuffer *payload = (PARCBuffer *) parcIterator_Next(itr); + + uint8_t *contents = parcBuffer_Overlay(payload, 0); + if (count < 32) { + for (size_t i = 0; i < 32; i++) { + assertTrue(contents[i] == count, "Expected %zu at index %zu, got %d", count, i, contents[i]); + } + } else { + for (size_t i = 0; i < 6; i++) { + assertTrue(contents[i] == 0xFF, "Expected %zu at index %zu, got %d", (size_t) 0xFF, i, contents[i]); + } + } + count++; + + parcBuffer_Release(&payload); + } + assertTrue(count == 33, "Expected to iterate over 33 content objects from the chunker, but for %zu", count); + parcIterator_Release(&itr); + + parcFile_Release(&file); + parcFileChunker_Release(&chunker); + + _deleteFile("/tmp/file_chunker.tmp"); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parc_Chunker_ForwardIterator_FileSmall) +{ + PARCBuffer *buffer = parcBuffer_Allocate(16); + + // Special 0xFF to mark the end... + for (int i = 0; i < 16; i++) { + parcBuffer_PutUint8(buffer, 0xFF); + } + parcBuffer_Flip(buffer); + + _createFile("/tmp/file_chunker.tmp", buffer); + + PARCFile *file = parcFile_Create("/tmp/file_chunker.tmp"); + + PARCFileChunker *chunker = parcFileChunker_Create(file, 4096); // each chunk is 1024 bytes + assertNotNull(chunker, "Expected non-NULL Chunker"); + + PARCIterator *itr = parcFileChunker_ForwardIterator(chunker); + size_t count = 0; + while (parcIterator_HasNext(itr)) { + PARCBuffer *payload = (PARCBuffer *) parcIterator_Next(itr); + + uint8_t *contents = parcBuffer_Overlay(payload, 0); + for (size_t i = 0; i < 16; i++) { + assertTrue(contents[i] == 0xFF, "Expected %zu at index %zu, got %d", (size_t) 0xFF, i, contents[i]); + } + count++; + + parcBuffer_Release(&payload); + } + assertTrue(count == 1, "Expected to iterate over 1 content objects from the chunker, but for %zu", count); + parcIterator_Release(&itr); + + parcFile_Release(&file); + parcFileChunker_Release(&chunker); + + _deleteFile("/tmp/file_chunker.tmp"); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parc_Chunker_ReverseIterator_File) +{ + PARCBuffer *buffer = parcBuffer_Allocate(1024); + + for (size_t i = 0; i < 32; i++) { + for (size_t j = 0; j < 32; j++) { + parcBuffer_PutUint8(buffer, i); + } + } + parcBuffer_Flip(buffer); + + _createFile("/tmp/file_chunker.tmp", buffer); + + PARCFile *file = parcFile_Create("/tmp/file_chunker.tmp"); + + PARCFileChunker *chunker = parcFileChunker_Create(file, 32); // each chunk is 32 bytes + assertNotNull(chunker, "Expected non-NULL Chunker"); + + PARCIterator *itr = parcFileChunker_ReverseIterator(chunker); + size_t count = 0; + while (parcIterator_HasNext(itr)) { + PARCBuffer *payload = (PARCBuffer *) parcIterator_Next(itr); + + uint8_t *contents = parcBuffer_Overlay(payload, 0); + for (size_t i = 0; i < 32; i++) { + assertTrue(contents[i] == (31 - count), "Expected %zu at index %zu, got %d", count, i, contents[i]); + } + count++; + + parcBuffer_Release(&payload); + } + assertTrue(count == 32, "Expected to iterate over 32 content objects from the chunker, but for %zu", count); + parcIterator_Release(&itr); + + parcFile_Release(&file); + parcFileChunker_Release(&chunker); + + _deleteFile("/tmp/file_chunker.tmp"); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parc_Chunker_ReverseIterator_FilePartial) +{ + PARCBuffer *buffer = parcBuffer_Allocate(1030); + + // Special 0xFF to mark the start... + for (int i = 0; i < 6; i++) { + parcBuffer_PutUint8(buffer, 0xFF); + } + + for (size_t i = 0; i < 32; i++) { + for (size_t j = 0; j < 32; j++) { + parcBuffer_PutUint8(buffer, i); + } + } + parcBuffer_Flip(buffer); + + _createFile("/tmp/file_chunker.tmp", buffer); + + PARCFile *file = parcFile_Create("/tmp/file_chunker.tmp"); + + PARCFileChunker *chunker = parcFileChunker_Create(file, 32); // each chunk is 32 bytes + assertNotNull(chunker, "Expected non-NULL Chunker"); + + PARCIterator *itr = parcFileChunker_ReverseIterator(chunker); + size_t count = 0; + while (parcIterator_HasNext(itr)) { + PARCBuffer *payload = (PARCBuffer *) parcIterator_Next(itr); + + uint8_t *contents = parcBuffer_Overlay(payload, 0); + if (count < 32) { + for (size_t i = 0; i < 32; i++) { + assertTrue(contents[i] == (31 - count), "Expected %zu at index %zu, got %d", (31 - count), i, contents[i]); + } + } else { + for (size_t i = 0; i < 6; i++) { + assertTrue(contents[i] == 0xFF, "Expected %zu at index %zu, got %d", (size_t) 0xFF, i, contents[i]); + } + } + count++; + + parcBuffer_Release(&payload); + } + assertTrue(count == 33, "Expected to iterate over 33 content objects from the chunker, but for %zu", count); + parcIterator_Release(&itr); + + parcFile_Release(&file); + parcFileChunker_Release(&chunker); + + _deleteFile("/tmp/file_chunker.tmp"); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parc_Chunker_ReverseIterator_FileSmall) +{ + PARCBuffer *buffer = parcBuffer_Allocate(1030); + + // Special 0xFF to mark the start... + for (int i = 0; i < 6; i++) { + parcBuffer_PutUint8(buffer, 0xFF); + } + parcBuffer_Flip(buffer); + + _createFile("/tmp/file_chunker.tmp", buffer); + + PARCFile *file = parcFile_Create("/tmp/file_chunker.tmp"); + + PARCFileChunker *chunker = parcFileChunker_Create(file, 32); // each chunk is 32 bytes + assertNotNull(chunker, "Expected non-NULL Chunker"); + + PARCIterator *itr = parcFileChunker_ReverseIterator(chunker); + size_t count = 0; + while (parcIterator_HasNext(itr)) { + PARCBuffer *payload = (PARCBuffer *) parcIterator_Next(itr); + + uint8_t *contents = parcBuffer_Overlay(payload, 0); + for (size_t i = 0; i < 6; i++) { + assertTrue(contents[i] == 0xFF, "Expected %zu at index %zu, got %d", (size_t) 0xFF, i, contents[i]); + } + count++; + + parcBuffer_Release(&payload); + } + assertTrue(count == 1, "Expected to iterate over 1 content objects from the chunker, but for %zu", count); + parcIterator_Release(&itr); + + parcFile_Release(&file); + parcFileChunker_Release(&chunker); + + _deleteFile("/tmp/file_chunker.tmp"); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parc_Chunker_GetChunkSize) +{ + size_t expectedChunkSize = 32; + PARCBuffer *buffer = parcBuffer_Allocate(1030); + + // Special 0xFF to mark the start... + for (int i = 0; i < 6; i++) { + parcBuffer_PutUint8(buffer, 0xFF); + } + parcBuffer_Flip(buffer); + + _createFile("/tmp/file_chunker.tmp", buffer); + + PARCFile *file = parcFile_Create("/tmp/file_chunker.tmp"); + PARCFileChunker *chunker = parcFileChunker_Create(file, expectedChunkSize); // each chunk is 32 bytes + + size_t actualChunkSize = parcBufferChunker_GetChunkSize(chunker); + + assertTrue(actualChunkSize == expectedChunkSize, "Expected chunk size of %zu, got %zu", expectedChunkSize, actualChunkSize); + + parcFile_Release(&file); + parcFileChunker_Release(&chunker); + + _deleteFile("/tmp/file_chunker.tmp"); + + parcBuffer_Release(&buffer); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_FileChunker); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_FileInputStream.c b/libparc/parc/algol/test/test_parc_FileInputStream.c new file mode 100755 index 00000000..49032050 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_FileInputStream.c @@ -0,0 +1,107 @@ +/* + * 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. + */ + +/** + * @header <#Headline Name#> + * @abstract <#Abstract#> + * @discussion + * <#Discussion#> + * + */ +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../parc_FileInputStream.c" + +#include <LongBow/unit-test.h> +#include <LongBow/debugging.h> + +#include <parc/algol/parc_File.h> +#include <parc/algol/parc_SafeMemory.h> + +LONGBOW_TEST_RUNNER(test_parc_FileInputStream) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(test_parc_FileInputStream) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(test_parc_FileInputStream) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcFileInputStream_Open); + LONGBOW_RUN_TEST_CASE(Global, parcFileInputStream_ReadFile); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcFileInputStream_Open) +{ + PARCFile *file = parcFile_Create("test_parc_FileInputStream"); + PARCFileInputStream *stream = parcFileInputStream_Open(file); + + parcFileInputStream_Release(&stream); + parcFile_Release(&file); +} + +LONGBOW_TEST_CASE(Global, parcFileInputStream_ReadFile) +{ + PARCFile *file = parcFile_Create("test_parc_FileInputStream"); + PARCFileInputStream *stream = parcFileInputStream_Open(file); + + PARCBuffer *actual = parcFileInputStream_ReadFile(stream); + assertNotNull(actual, "Expected non-null result from parcFileInputStream_ReadFile"); + + parcBuffer_Flip(actual); + + assertTrue(parcBuffer_HasRemaining(actual), "Expected the buffer to contain data."); + + parcBuffer_Release(&actual); + parcFileInputStream_Release(&stream); + parcFile_Release(&file); +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(test_parc_FileInputStream); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_FileOutputStream.c b/libparc/parc/algol/test/test_parc_FileOutputStream.c new file mode 100755 index 00000000..aa4236ae --- /dev/null +++ b/libparc/parc/algol/test/test_parc_FileOutputStream.c @@ -0,0 +1,167 @@ +/* + * 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 <LongBow/unit-test.h> +#include <fcntl.h> + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../parc_SafeMemory.h" +#include "../parc_FileOutputStream.c" + +#define PATH_SEGMENT "A" + +LONGBOW_TEST_RUNNER(parc_FileOutputStream) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Local); + LONGBOW_RUN_TEST_FIXTURE(AcquireRelease); + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_FileOutputStream) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_FileOutputStream) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(AcquireRelease) +{ + LONGBOW_RUN_TEST_CASE(AcquireRelease, parcFileOutputStream_Create); + LONGBOW_RUN_TEST_CASE(AcquireRelease, parcFileOutputStream_Release); + LONGBOW_RUN_TEST_CASE(AcquireRelease, parcFileOutputStream_AcquireRelease); +} + +LONGBOW_TEST_FIXTURE_SETUP(AcquireRelease) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(AcquireRelease) +{ + unlink("/tmp/test_parc_FileOutputStream"); + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(AcquireRelease, parcFileOutputStream_Create) +{ + PARCFileOutputStream *stream = parcFileOutputStream_Create(open("/tmp/test_parc_FileOutputStream", O_CREAT | O_WRONLY | O_TRUNC, 0600)); + assertNotNull(stream, "Expected a non-null pointer"); + + parcFileOutputStream_Release(&stream); + assertNull(stream, "Expected parcFileOutputStream_Release to null the pointer"); + unlink("/tmp/test_parc_FileOutputStream"); +} + +LONGBOW_TEST_CASE(AcquireRelease, parcFileOutputStream_Release) +{ + PARCFileOutputStream *stream = parcFileOutputStream_Create(open("/tmp/test_parc_FileOutputStream", O_CREAT | O_WRONLY | O_TRUNC, 0600)); + assertNotNull(stream, "Expected a non-null pointer"); + + parcFileOutputStream_Release(&stream); + assertNull(stream, "Expected parcFileOutputStream_Release to null the pointer"); + unlink("/tmp/test_parc_FileOutputStream"); +} + +LONGBOW_TEST_CASE(AcquireRelease, parcFileOutputStream_AcquireRelease) +{ + PARCFileOutputStream *stream = parcFileOutputStream_Create(open("/tmp/test_parc_FileOutputStream", O_CREAT | O_WRONLY | O_TRUNC, 0600)); + assertNotNull(stream, "Expected a non-null pointer"); + + PARCFileOutputStream *reference = parcFileOutputStream_Acquire(stream); + assertTrue(stream == reference, "Expected the reference to be equal to the original."); + + parcFileOutputStream_Release(&stream); + assertNull(stream, "Expected parcFileOutputStream_Release to null the pointer"); + + parcFileOutputStream_Release(&reference); + unlink("/tmp/test_parc_FileOutputStream"); +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcFileOutputStream_Write); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + unlink("/tmp/test_parc_FileOutputStream"); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcFileOutputStream_Write) +{ + PARCFileOutputStream *stream = + parcFileOutputStream_Create(open("/tmp/test_parc_FileOutputStream", O_CREAT | O_WRONLY | O_TRUNC, 0600)); + + PARCBuffer *buffer = parcBuffer_Allocate(16 * 1024 * 1024); + + parcFileOutputStream_Write(stream, buffer); + + assertFalse(parcBuffer_HasRemaining(buffer), "Expected the buffer to be emtpy"); + + parcBuffer_Release(&buffer); + + parcFileOutputStream_Release(&stream); + unlink("/tmp/test_parc_FileOutputStream"); +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_FileOutputStream); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_Hash.c b/libparc/parc/algol/test/test_parc_Hash.c new file mode 100755 index 00000000..cc92a867 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_Hash.c @@ -0,0 +1,253 @@ +/* + * 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 "../parc_Hash.c" + +#include <LongBow/testing.h> +#include <stdio.h> + +#include <parc/algol/parc_SafeMemory.h> + +LONGBOW_TEST_RUNNER(parc_Hash) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_Hash) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_Hash) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcHash32Bit_Create); + LONGBOW_RUN_TEST_CASE(Global, parcHash32Bit_Acquire); + LONGBOW_RUN_TEST_CASE(Global, parcHash32Bit_Release); + LONGBOW_RUN_TEST_CASE(Global, parcHash32Bit_Update); + LONGBOW_RUN_TEST_CASE(Global, parcHash32Bit_UpdateUint32); + LONGBOW_RUN_TEST_CASE(Global, parcHash32Bit_Hash); + + LONGBOW_RUN_TEST_CASE(Global, parc_Hash32_Data); + LONGBOW_RUN_TEST_CASE(Global, parc_Hash32_Int32); + LONGBOW_RUN_TEST_CASE(Global, parc_Hash32_Int64); + LONGBOW_RUN_TEST_CASE(Global, parc_Hash64_Data); + LONGBOW_RUN_TEST_CASE(Global, parc_Hash64_Int32); + LONGBOW_RUN_TEST_CASE(Global, parc_Hash64_Int64); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcHash32Bit_Create) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_CASE(Global, parcHash32Bit_Acquire) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_CASE(Global, parcHash32Bit_Release) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_CASE(Global, parcHash32Bit_Update) +{ + PARCHash32Bits *hash = parcHash32Bits_Create(); + parcHash32Bits_Update(hash, "123", 3); + uint32_t value = parcHash32Bits_Hash(hash); + assertTrue(value != 0, "Expected a non-zero (non-initial) value"); + + parcHash32Bits_Release(&hash); +} + +LONGBOW_TEST_CASE(Global, parcHash32Bit_UpdateUint32) +{ + PARCHash32Bits *hash = parcHash32Bits_Create(); + parcHash32Bits_UpdateUint32(hash, 123); + uint32_t value = parcHash32Bits_Hash(hash); + assertTrue(value != 0, "Expected a non-zero (non-initial) value"); + + parcHash32Bits_Release(&hash); +} + +LONGBOW_TEST_CASE(Global, parcHash32Bit_Hash) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_CASE(Global, parc_Hash32_Data) +{ + char *data1 = "Hello World"; + char *data2 = "Hello World1"; + char *data3 = "Hello World2"; + + char data4[20]; + strncpy(data4, data1, sizeof(data4)); + + uint32_t hash1 = parcHash32_Data(data1, strlen(data1)); + uint32_t hash2 = parcHash32_Data(data2, strlen(data2)); + uint32_t hash3 = parcHash32_Data(data3, strlen(data3)); + uint32_t hash4 = parcHash32_Data(data4, strlen(data4)); + + assertTrue(hash1 != 0, "Hash is 0, unlikely"); + assertTrue(hash2 != 0, "Hash is 0, unlikely"); + assertTrue(hash3 != 0, "Hash is 0, unlikely"); + assertTrue(hash4 != 0, "Hash is 0, unlikely"); + assertTrue(hash1 != hash2, "Hash collision, unlikely"); + assertTrue(hash3 != hash2, "Hash collision, unlikely"); + assertTrue(hash3 != hash1, "Hash collision, unlikely"); + assertTrue(hash1 == hash4, "Hash different for same content"); +} + +LONGBOW_TEST_CASE(Global, parc_Hash32_Int32) +{ + uint32_t data1 = 12345; + uint32_t data2 = 12346; + uint32_t data3 = 12345; + + uint32_t hash1 = parcHash32_Int32(data1); + uint32_t hash2 = parcHash32_Int32(data2); + uint32_t hash3 = parcHash32_Int32(data3); + + assertTrue(hash1 != 0, "Hash is 0, unlikely"); + assertTrue(hash2 != 0, "Hash is 0, unlikely"); + assertTrue(hash3 != 0, "Hash is 0, unlikely"); + assertTrue(hash1 != hash2, "Hash collision, unlikely"); + assertTrue(hash1 == hash3, "Hash different for same content"); +} + +LONGBOW_TEST_CASE(Global, parc_Hash32_Int64) +{ + uint64_t data1 = 10010010012345; + uint64_t data2 = 10010010012346; + uint64_t data3 = 10010010012345; + + uint32_t hash1 = parcHash32_Int64(data1); + uint32_t hash2 = parcHash32_Int64(data2); + uint32_t hash3 = parcHash32_Int64(data3); + + assertTrue(hash1 != 0, "Hash is 0, unlikely"); + assertTrue(hash2 != 0, "Hash is 0, unlikely"); + assertTrue(hash3 != 0, "Hash is 0, unlikely"); + assertTrue(hash1 != hash2, "Hash collision, unlikely"); + assertTrue(hash1 == hash3, "Hash different for same content"); +} + +LONGBOW_TEST_CASE(Global, parc_Hash64_Data) +{ + char *data1 = "Hello World"; + char *data2 = "Hello World1"; + char *data3 = "Hello World2"; + + char data4[20]; + strncpy(data4, data1, sizeof(data4)); + + uint64_t hash1 = parcHash64_Data(data1, strlen(data1)); + uint64_t hash2 = parcHash64_Data(data2, strlen(data2)); + uint64_t hash3 = parcHash64_Data(data3, strlen(data3)); + uint64_t hash4 = parcHash64_Data(data4, strlen(data4)); + + assertTrue(hash1 != 0, "Hash is 0, unlikely"); + assertTrue(hash2 != 0, "Hash is 0, unlikely"); + assertTrue(hash3 != 0, "Hash is 0, unlikely"); + assertTrue(hash4 != 0, "Hash is 0, unlikely"); + assertTrue(hash1 != hash2, "Hash collision, unlikely"); + assertTrue(hash3 != hash2, "Hash collision, unlikely"); + assertTrue(hash3 != hash1, "Hash collision, unlikely"); + assertTrue(hash1 == hash4, "Hash different for same content"); +} + +LONGBOW_TEST_CASE(Global, parc_Hash64_Int32) +{ + uint32_t data1 = 12345; + uint32_t data2 = 12346; + uint32_t data3 = 12345; + + uint64_t hash1 = parcHash64_Int32(data1); + uint64_t hash2 = parcHash64_Int32(data2); + uint64_t hash3 = parcHash64_Int32(data3); + + assertTrue(hash1 != 0, "Hash is 0, unlikely"); + assertTrue(hash2 != 0, "Hash is 0, unlikely"); + assertTrue(hash3 != 0, "Hash is 0, unlikely"); + assertTrue(hash1 != hash2, "Hash collision, unlikely"); + assertTrue(hash1 == hash3, "Hash different for same content"); +} + +LONGBOW_TEST_CASE(Global, parc_Hash64_Int64) +{ + uint64_t data1 = 10010010012345; + uint64_t data2 = 10010010012346; + uint64_t data3 = 10010010012345; + + uint64_t hash1 = parcHash64_Int64(data1); + uint64_t hash2 = parcHash64_Int64(data2); + uint64_t hash3 = parcHash64_Int64(data3); + + assertTrue(hash1 != 0, "Hash is 0, unlikely"); + assertTrue(hash2 != 0, "Hash is 0, unlikely"); + assertTrue(hash3 != 0, "Hash is 0, unlikely"); + assertTrue(hash1 != hash2, "Hash collision, unlikely"); + assertTrue(hash1 == hash3, "Hash different for same content"); +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_Hash); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_HashCode.c b/libparc/parc/algol/test/test_parc_HashCode.c new file mode 100755 index 00000000..a5b21137 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_HashCode.c @@ -0,0 +1,109 @@ +/* + * 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 the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../parc_HashCode.c" + +#include <LongBow/testing.h> +#include <LongBow/debugging.h> + +LONGBOW_TEST_RUNNER(test_parc_HashCode) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(test_parc_HashCode) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(test_parc_HashCode) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcHashCode_HashImpl); + LONGBOW_RUN_TEST_CASE(Global, parcHashCode_HashHashCode); + LONGBOW_RUN_TEST_CASE(Global, parcHashCode_Hash); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcHashCode_HashImpl) +{ + PARCHashCode lastValue = 0; + uint8_t *memory = (uint8_t *) "1234"; + size_t length = 4; + + PARCHashCode expected = 3316911679945239212ULL; + PARCHashCode actual = parcHashCode_HashImpl(memory, length, lastValue); + + assertTrue(expected == actual, "Expected %" PRIPARCHashCode " actual %" PRIPARCHashCode, expected, actual); +} + +LONGBOW_TEST_CASE(Global, parcHashCode_HashHashCode) +{ + PARCHashCode lastValue = 0; + uint8_t *memory = (uint8_t *) "1234"; + size_t length = 4; + + PARCHashCode lastHash = parcHashCode_HashImpl(memory, length, lastValue); + + PARCHashCode hashedHash = parcHashCode_HashHashCode(lastHash, 123456); + + assertTrue(lastHash != 0, "Expected a non zero hash value for lastHash"); + assertTrue(hashedHash != 0, "Expected a non zero hash value for hashedHash"); + assertTrue(lastHash != hashedHash, "Expected different hash values for hashedHash and lastHash"); +} + +LONGBOW_TEST_CASE(Global, parcHashCode_Hash) +{ + char *testString1 = "this is some test data"; + char *testString2 = "this is different test data"; + + PARCHashCode hash1 = parcHashCode_Hash((uint8_t *) testString1, strlen(testString1)); + PARCHashCode hash2 = parcHashCode_Hash((uint8_t *) testString2, strlen(testString2)); + + assertTrue(hash1 != 0, "Expected a non zero hash value for testString1"); + assertTrue(hash2 != 0, "Expected a non zero hash value for testString2"); + assertTrue(hash1 != hash2, "Expected different hash values for testString1 and testString2"); +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(test_parc_HashCode); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_HashCodeTable.c b/libparc/parc/algol/test/test_parc_HashCodeTable.c new file mode 100755 index 00000000..c2adf9d2 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_HashCodeTable.c @@ -0,0 +1,423 @@ +/* + * 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 the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include <config.h> + +#include <time.h> +#include <fcntl.h> +#include <errno.h> + +#include <sys/time.h> + +#include <LongBow/unit-test.h> + +#include "../parc_HashCodeTable.c" + +#include <parc/algol/parc_SafeMemory.h> + +// ============================== +// The objects to put in the hash table. We have a separate key class and data class + +typedef struct test_key_class { + unsigned key_value; + unsigned hash_value; +} TestKeyClass; + +typedef struct test_data_class { + unsigned data_value; +} TestDataClass; + +static bool +TestKeyClass_Equals(const void *a, const void *b) +{ + return ((TestKeyClass *) a)->key_value == ((TestKeyClass *) b)->key_value; +} + +static HashCodeType +TestKeyClass_Hash(const void *a) +{ + return ((TestKeyClass *) a)->hash_value; +} + +static void +TestKeyClassDestroy(void **aPtr) +{ + parcMemory_Deallocate((void **) aPtr); + aPtr = NULL; +} + +static void +TestDataClassDestroy(void **aPtr) +{ + parcMemory_Deallocate((void **) aPtr); + aPtr = NULL; +} + +typedef struct truth_table_entry { + unsigned key_value; + unsigned hash_code; + unsigned data_value; +} TruthTableEntry; + +// ============================== + + +LONGBOW_TEST_RUNNER(parc_HashCodeTable) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_HashCodeTable) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_HashCodeTable) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcHashCodeTable_Add_Get); + LONGBOW_RUN_TEST_CASE(Global, parcHashCodeTable_Create); + LONGBOW_RUN_TEST_CASE(Global, parcHashCodeTable_Create_Size); + LONGBOW_RUN_TEST_CASE(Global, parcHashCodeTable_Del); + + LONGBOW_RUN_TEST_CASE(Global, parcHashCodeTable_Add_DuplicateHashes); + LONGBOW_RUN_TEST_CASE(Global, parcHashCodeTable_Add_DuplicateValues); + + LONGBOW_RUN_TEST_CASE(Global, parcHashCodeTable_BigTable); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + printf("('%s' leaks memory by %d (allocs - frees)) ", longBowTestCase_GetName(testCase), parcMemory_Outstanding()); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcHashCodeTable_Add_Get) +{ + const int testsize = 4096; + PARCHashCodeTable *table = parcHashCodeTable_Create(TestKeyClass_Equals, TestKeyClass_Hash, TestKeyClassDestroy, TestDataClassDestroy); + TruthTableEntry *truthtable = parcMemory_Allocate(sizeof(TruthTableEntry) * testsize); + assertNotNull(truthtable, "parcMemory_Allocate(%zu) returned NULL", sizeof(TruthTableEntry) * testsize); + int i; + int fd = open("/dev/urandom", O_RDONLY); + + if (fd == -1) { + assertFalse(fd == -1, "Error opening random number generator: %s", strerror(errno)); + } + + ssize_t nread = read(fd, truthtable, sizeof(TruthTableEntry) * testsize); + assertTrue(nread > 0, "Error using read"); + close(fd); + + for (i = 0; i < testsize; i++) { + TestKeyClass *key = parcMemory_AllocateAndClear(sizeof(TestKeyClass)); + assertNotNull(key, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestKeyClass)); + TestDataClass *data = parcMemory_AllocateAndClear(sizeof(TestDataClass)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestDataClass)); + + key->key_value = truthtable[i].key_value; + key->hash_value = truthtable[i].hash_code; + data->data_value = truthtable[i].data_value; + + bool success = parcHashCodeTable_Add(table, key, data); + assertTrue(success, "Failed inserting value"); + } + + // now retrieve them + for (i = 0; i < testsize; i++) { + TestKeyClass lookupkey; + lookupkey.key_value = truthtable[i].key_value; + lookupkey.hash_value = truthtable[i].hash_code; + + TestDataClass *data = parcHashCodeTable_Get(table, &lookupkey); + assertTrue(data->data_value == truthtable[i].data_value, "Data value incorrect"); + } + + parcHashCodeTable_Destroy(&table); + parcMemory_Deallocate((void **) &truthtable); +} + +LONGBOW_TEST_CASE(Global, parcHashCodeTable_Create) +{ + PARCHashCodeTable *table = parcHashCodeTable_Create(TestKeyClass_Equals, TestKeyClass_Hash, TestKeyClassDestroy, TestDataClassDestroy); + + assertNotNull(table, "table came back as null"); + assertTrue(table->hashtable.tableSize == 0, "Hash table initialized to wrong size"); + assertTrue(table->hashtable.tableLimit == MIN_SIZE, "Initial table limit size is wrong"); + assertTrue(table->keyEqualsFunc == TestKeyClass_Equals, "KeyEqualsFunc wrong"); + assertTrue(table->keyHashCodeFunc == TestKeyClass_Hash, "KeyHashFunc wrong"); + assertTrue(table->keyDestroyer == TestKeyClassDestroy, "KeyDestroyer wrong"); + assertTrue(table->dataDestroyer == TestDataClassDestroy, "DataDestroyer wrong"); + + parcHashCodeTable_Destroy(&table); +} + +LONGBOW_TEST_CASE(Global, parcHashCodeTable_Create_Size) +{ + PARCHashCodeTable *table = parcHashCodeTable_Create_Size(TestKeyClass_Equals, TestKeyClass_Hash, TestKeyClassDestroy, TestDataClassDestroy, 16); + + assertNotNull(table, "table came back as null"); + assertTrue(table->hashtable.tableLimit == 16, "Initial table limit size is wrong"); + parcHashCodeTable_Destroy(&table); +} + +LONGBOW_TEST_CASE(Global, parcHashCodeTable_Del) +{ + const int testsize = 6; + PARCHashCodeTable *table = parcHashCodeTable_Create(TestKeyClass_Equals, TestKeyClass_Hash, TestKeyClassDestroy, TestDataClassDestroy); + TruthTableEntry *truthtable = parcMemory_Allocate(sizeof(TruthTableEntry) * testsize); + assertNotNull(truthtable, "parcMemory_Allocate(%zu) returned NULL", sizeof(TruthTableEntry) * testsize); + int i; + int fd = open("/dev/urandom", O_RDONLY); + + if (fd == -1) { + assertFalse(fd == -1, "Error opening random number generator: %s", strerror(errno)); + } + + ssize_t nread = read(fd, truthtable, sizeof(TruthTableEntry) * testsize); + assertTrue(nread > 0, "Error using read"); + close(fd); + + for (i = 0; i < testsize; i++) { + TestKeyClass *key = parcMemory_AllocateAndClear(sizeof(TestKeyClass)); + assertNotNull(key, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestKeyClass)); + TestDataClass *data = parcMemory_AllocateAndClear(sizeof(TestDataClass)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestDataClass)); + + key->key_value = truthtable[i].key_value; + key->hash_value = truthtable[i].hash_code; + data->data_value = truthtable[i].data_value; + + bool success = parcHashCodeTable_Add(table, key, data); + assertTrue(success, "Failed inserting value"); + } + + // delete the last one + { + TestKeyClass lookupkey; + + lookupkey.key_value = truthtable[testsize - 1].key_value; + lookupkey.hash_value = truthtable[testsize - 1].hash_code; + + parcHashCodeTable_Del(table, &lookupkey); + assertTrue(table->hashtable.tableSize == testsize - 1, "tableSize wrong"); + } + + for (i = 0; i < testsize - 1; i++) { + TestKeyClass lookupkey; + lookupkey.key_value = truthtable[i].key_value; + lookupkey.hash_value = truthtable[i].hash_code; + + TestDataClass *data = parcHashCodeTable_Get(table, &lookupkey); + assertTrue(data->data_value == truthtable[i].data_value, "Data value incorrect"); + } + + for (i = testsize - 1; i < testsize; i++) { + TestKeyClass lookupkey; + lookupkey.key_value = truthtable[i].key_value; + lookupkey.hash_value = truthtable[i].hash_code; + + TestDataClass *data = parcHashCodeTable_Get(table, &lookupkey); + assertNull(data, "Should not have returned deleted value"); + } + parcHashCodeTable_Destroy(&table); + parcMemory_Deallocate((void **) &truthtable); +} + +LONGBOW_TEST_CASE(Global, parcHashCodeTable_Add_DuplicateHashes) +{ + PARCHashCodeTable *table = parcHashCodeTable_Create(TestKeyClass_Equals, TestKeyClass_Hash, TestKeyClassDestroy, TestDataClassDestroy); + + TestKeyClass *key1 = parcMemory_AllocateAndClear(sizeof(TestKeyClass)); + assertNotNull(key1, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestKeyClass)); + TestKeyClass *key2 = parcMemory_AllocateAndClear(sizeof(TestKeyClass)); + assertNotNull(key2, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestKeyClass)); + TestDataClass *data1 = parcMemory_AllocateAndClear(sizeof(TestDataClass)); + assertNotNull(data1, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestDataClass)); + TestDataClass *data2 = parcMemory_AllocateAndClear(sizeof(TestDataClass)); + assertNotNull(data2, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestDataClass)); + TestDataClass *test; + bool success; + + *key1 = (TestKeyClass) { .key_value = 1, .hash_value = 2 }; + *key2 = (TestKeyClass) { .key_value = 3, .hash_value = 2 }; + data1->data_value = 11; + data2->data_value = 22; + + success = parcHashCodeTable_Add(table, key1, data1); + assertTrue(success, "Failed to add value"); + + success = parcHashCodeTable_Add(table, key2, data2); + assertTrue(success, "Failed to add value"); + + // value 3 should be in position 3 + test = parcHashCodeTable_Get(table, key1); + assertNotNull(test, "returned null on get"); + assertTrue(test->data_value == 11, "Got wrong value back for key1"); + + test = parcHashCodeTable_Get(table, key2); + assertNotNull(test, "returned null on get"); + assertTrue(test->data_value == 22, "Got wrong value back for key1"); + + parcHashCodeTable_Destroy(&table); +} + +LONGBOW_TEST_CASE(Global, parcHashCodeTable_Add_DuplicateValues) +{ + PARCHashCodeTable *table = parcHashCodeTable_Create(TestKeyClass_Equals, TestKeyClass_Hash, TestKeyClassDestroy, TestDataClassDestroy); + + TestKeyClass *key1 = parcMemory_AllocateAndClear(sizeof(TestKeyClass)); + assertNotNull(key1, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestKeyClass)); + TestKeyClass *key2 = parcMemory_AllocateAndClear(sizeof(TestKeyClass)); + assertNotNull(key2, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestKeyClass)); + TestDataClass *data1 = parcMemory_AllocateAndClear(sizeof(TestDataClass)); + assertNotNull(data1, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestDataClass)); + TestDataClass *data2 = parcMemory_AllocateAndClear(sizeof(TestDataClass)); + assertNotNull(data2, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestDataClass)); + TestDataClass *test; + bool success; + + *key1 = (TestKeyClass) { .key_value = 1, .hash_value = 2 }; + *key2 = (TestKeyClass) { .key_value = 1, .hash_value = 2 }; + data1->data_value = 11; + data2->data_value = 22; + + success = parcHashCodeTable_Add(table, key1, data1); + assertTrue(success, "Failed to add value"); + + success = parcHashCodeTable_Add(table, key2, data2); + assertFalse(success, "Second add should have failed on duplicate key"); + + // value 3 should be in position 3 + test = parcHashCodeTable_Get(table, key1); + assertNotNull(test, "returned null on get"); + assertTrue(test->data_value == 11, "Got wrong value back for key1"); + + parcHashCodeTable_Destroy(&table); + parcMemory_Deallocate((void **) &key2); + parcMemory_Deallocate((void **) &data2); +} + +LONGBOW_TEST_CASE(Global, parcHashCodeTable_BigTable) +{ + PARCHashCodeTable *table = parcHashCodeTable_Create(TestKeyClass_Equals, TestKeyClass_Hash, TestKeyClassDestroy, TestDataClassDestroy); + + struct timeval t0, t1; + + gettimeofday(&t0, NULL); + + int loops = 1000; + for (int i = 0; i < loops; i++) { + TestKeyClass *key = parcMemory_AllocateAndClear(sizeof(TestKeyClass)); + assertNotNull(key, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestKeyClass)); + TestDataClass *data = parcMemory_AllocateAndClear(sizeof(TestDataClass)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestDataClass)); + + *key = (TestKeyClass) { .key_value = i, .hash_value = i }; + data->data_value = i; + + bool success = parcHashCodeTable_Add(table, key, data); + assertTrue(success, "Failed to add value"); + } + gettimeofday(&t1, NULL); + + timersub(&t1, &t0, &t1); + double sec = t1.tv_sec + t1.tv_usec * 1E-6; + printf("expand count %u, sec = %.3f, sec/add = %.9f\n", table->expandCount, sec, sec / loops); + + gettimeofday(&t0, NULL); + parcHashCodeTable_Destroy(&table); + gettimeofday(&t1, NULL); + + timersub(&t1, &t0, &t1); + sec = t1.tv_sec + t1.tv_usec * 1E-6; + printf("destroy sec = %.3f, sec/add = %.9f\n", sec, sec / loops); +} + +LONGBOW_TEST_FIXTURE(Local) +{ + LONGBOW_RUN_TEST_CASE(Local, _findIndex); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + printf("('%s' leaks memory by %d (allocs - frees)) ", longBowTestCase_GetName(testCase), parcMemory_Outstanding()); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Local, _findIndex) +{ + PARCHashCodeTable *table = parcHashCodeTable_Create(TestKeyClass_Equals, TestKeyClass_Hash, TestKeyClassDestroy, TestDataClassDestroy); + + // stick a data element in the middle of the table + TestKeyClass *key = parcMemory_AllocateAndClear(sizeof(TestKeyClass)); + assertNotNull(key, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestKeyClass)); + TestDataClass *data = parcMemory_AllocateAndClear(sizeof(TestDataClass)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestDataClass)); + + key->key_value = 1; + key->hash_value = 37; + data->data_value = 7; + + table->hashtable.entries[37].key = key; + table->hashtable.entries[37].hashcode = key->hash_value; + table->hashtable.entries[37].data = data; + + size_t index; + bool success = _findIndex(table, key, &index); + assertTrue(success, "FindIndex did not find known value"); + assertTrue(index == 37, "FindIndex returned wrong value"); + + + parcHashCodeTable_Destroy(&table); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_HashCodeTable); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_HashMap.c b/libparc/parc/algol/test/test_parc_HashMap.c new file mode 100644 index 00000000..1b071f44 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_HashMap.c @@ -0,0 +1,915 @@ +/* + * 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 "../parc_HashMap.c" + +#include <LongBow/unit-test.h> +#include <parc/algol/parc_SafeMemory.h> +#include <parc/algol/parc_StdlibMemory.h> + +#include <parc/testing/parc_ObjectTesting.h> +#include <parc/testing/parc_MemoryTesting.h> + +LONGBOW_TEST_RUNNER(parc_HashMap) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Static); + LONGBOW_RUN_TEST_FIXTURE(CreateAcquireRelease); + LONGBOW_RUN_TEST_FIXTURE(ObjectContract); + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_HashMap) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_HashMap) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(CreateAcquireRelease) +{ + LONGBOW_RUN_TEST_CASE(CreateAcquireRelease, CreateRelease); + LONGBOW_RUN_TEST_CASE(CreateAcquireRelease, CreateCapacity0); + LONGBOW_RUN_TEST_CASE(CreateAcquireRelease, CreateCapacityNominal); +} + +LONGBOW_TEST_FIXTURE_SETUP(CreateAcquireRelease) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(CreateAcquireRelease) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(CreateAcquireRelease, CreateRelease) +{ + PARCHashMap *instance = parcHashMap_Create(); + assertNotNull(instance, "Expeced non-null result from parcHashMap_Create();"); + parcObjectTesting_AssertAcquireReleaseContract(parcHashMap_Acquire, instance); + + parcHashMap_Release(&instance); + assertNull(instance, "Expeced null result from parcHashMap_Release();"); +} + +LONGBOW_TEST_CASE(CreateAcquireRelease, CreateCapacity0) +{ + const size_t CAPACITY = 0; + PARCHashMap *instance = parcHashMap_CreateCapacity(CAPACITY); + assertNotNull(instance, "Expeced non-null result from parcHashMap_Create();"); + parcObjectTesting_AssertAcquireReleaseContract(parcHashMap_Acquire, instance); + + parcHashMap_Release(&instance); +} + +LONGBOW_TEST_CASE(CreateAcquireRelease, CreateCapacityNominal) +{ + const size_t CAPACITY = 10000; + PARCHashMap *instance = parcHashMap_CreateCapacity(CAPACITY); + assertNotNull(instance, "Expeced non-null result from parcHashMap_Create();"); + parcObjectTesting_AssertAcquireReleaseContract(parcHashMap_Acquire, instance); + assertTrue(instance->capacity == CAPACITY, "Expect capacity to be %zu", CAPACITY); + assertTrue(instance->size == 0, "Expect size to be 0"); + + //Make sure all the buckets exist + for (size_t i = 0; i < CAPACITY; ++i) { + assertNull(instance->buckets[i], "Expect the hashmap to be clear"); + } + + parcHashMap_Release(&instance); + assertNull(instance, "Expeced null result from parcHashMap_Release();"); +} + +LONGBOW_TEST_FIXTURE(ObjectContract) +{ + LONGBOW_RUN_TEST_CASE(ObjectContract, parcHashMap_Copy); + LONGBOW_RUN_TEST_CASE(ObjectContract, parcHashMap_Display); + LONGBOW_RUN_TEST_CASE(ObjectContract, parcHashMap_Equals); + LONGBOW_RUN_TEST_CASE(ObjectContract, parcHashMap_HashCode_Empty); + LONGBOW_RUN_TEST_CASE(ObjectContract, parcHashMap_HashCode_NonEmpty); + LONGBOW_RUN_TEST_CASE(ObjectContract, parcHashMap_IsValid); + LONGBOW_RUN_TEST_CASE(ObjectContract, parcHashMap_AssertValid); + LONGBOW_RUN_TEST_CASE(ObjectContract, parcHashMap_ToJSON); + LONGBOW_RUN_TEST_CASE(ObjectContract, parcHashMap_ToString); +} + +LONGBOW_TEST_FIXTURE_SETUP(ObjectContract) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(ObjectContract) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) { + parcSafeMemory_ReportAllocation(1); + + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(ObjectContract, parcHashMap_Copy) +{ + PARCHashMap *instance = parcHashMap_Create(); + PARCHashMap *copy = parcHashMap_Copy(instance); + + assertTrue(parcHashMap_Equals(instance, copy), "Expected the copy to be equal to the original"); + + parcHashMap_Release(&instance); + parcHashMap_Release(©); +} + +LONGBOW_TEST_CASE(ObjectContract, parcHashMap_Display) +{ + PARCHashMap *x = parcHashMap_Create(); + PARCBuffer *key = parcBuffer_WrapCString("key1"); + PARCBuffer *value = parcBuffer_WrapCString("value1"); + parcHashMap_Put(x, key, value); + parcHashMap_Display(x, 0); + + parcBuffer_Release(&key); + parcBuffer_Release(&value); + + parcHashMap_Release(&x); +} + +LONGBOW_TEST_CASE(ObjectContract, parcHashMap_Equals) +{ + PARCBuffer *key = parcBuffer_WrapCString("key1"); + PARCBuffer *value = parcBuffer_WrapCString("value1"); + + PARCHashMap *x = parcHashMap_Create(); + parcHashMap_Put(x, key, value); + PARCHashMap *y = parcHashMap_Create(); + parcHashMap_Put(y, key, value); + PARCHashMap *z = parcHashMap_Create(); + parcHashMap_Put(z, key, value); + + PARCHashMap *u1 = parcHashMap_Create(); + + PARCHashMap *u2 = parcHashMap_Create(); + parcHashMap_Put(u2, key, value); + + parcObjectTesting_AssertEquals(x, y, z, u1, NULL); + + parcHashMap_Release(&x); + parcHashMap_Release(&y); + parcHashMap_Release(&z); + parcHashMap_Release(&u1); + parcHashMap_Release(&u2); + + parcBuffer_Release(&key); + parcBuffer_Release(&value); +} + +LONGBOW_TEST_CASE(ObjectContract, parcHashMap_HashCode_Empty) +{ + PARCHashMap *instance = parcHashMap_Create(); + + PARCHashCode code = parcHashMap_HashCode(instance); + + assertTrue(code == 0, "Expected 0, actual %" PRIPARCHashCode, code); + parcHashMap_Release(&instance); +} + +LONGBOW_TEST_CASE(ObjectContract, parcHashMap_HashCode_NonEmpty) +{ + PARCHashMap *instance = parcHashMap_Create(); + PARCBuffer *key = parcBuffer_WrapCString("key1"); + PARCBuffer *value = parcBuffer_WrapCString("value1"); + parcHashMap_Put(instance, key, value); + parcBuffer_Release(&key); + parcBuffer_Release(&value); + + PARCHashCode code = parcHashMap_HashCode(instance); + assertTrue(code != 0, "Expected a non-zero hash code, actual %" PRIPARCHashCode, code); + + parcHashMap_Release(&instance); +} + +LONGBOW_TEST_CASE(ObjectContract, parcHashMap_IsValid) +{ + PARCHashMap *instance = parcHashMap_Create(); + + PARCBuffer *key = parcBuffer_WrapCString("key1"); + PARCBuffer *value = parcBuffer_WrapCString("value1"); + parcHashMap_Put(instance, key, value); + assertTrue(parcHashMap_IsValid(instance), "Expected parcHashMap_Create to result in a valid instance."); + + parcHashMap_Release(&instance); + assertFalse(parcHashMap_IsValid(instance), "Expected parcHashMap_Create to result in an invalid instance."); + + parcBuffer_Release(&key); + parcBuffer_Release(&value); +} + +LONGBOW_TEST_CASE(ObjectContract, parcHashMap_AssertValid) +{ + PARCHashMap *instance = parcHashMap_Create(); + + PARCBuffer *key = parcBuffer_WrapCString("key1"); + PARCBuffer *value = parcBuffer_WrapCString("value1"); + parcHashMap_Put(instance, key, value); + parcHashMap_AssertValid(instance); + + parcBuffer_Release(&key); + parcBuffer_Release(&value); + parcHashMap_Release(&instance); +} + +LONGBOW_TEST_CASE(ObjectContract, parcHashMap_ToJSON) +{ + PARCHashMap *instance = parcHashMap_Create(); + PARCBuffer *key = parcBuffer_WrapCString("key1"); + PARCBuffer *value = parcBuffer_WrapCString("value1"); + parcHashMap_Put(instance, key, value); + + PARCJSON *json = parcHashMap_ToJSON(instance); + + parcJSON_Release(&json); + parcBuffer_Release(&key); + parcBuffer_Release(&value); + + parcHashMap_Release(&instance); +} + +LONGBOW_TEST_CASE(ObjectContract, parcHashMap_ToString) +{ + PARCHashMap *instance = parcHashMap_Create(); + PARCBuffer *key = parcBuffer_WrapCString("key1"); + PARCBuffer *value = parcBuffer_WrapCString("value1"); + parcHashMap_Put(instance, key, value); + + char *string = parcHashMap_ToString(instance); + + assertNotNull(string, "Expected non-NULL result from parcHashMap_ToString"); + + parcMemory_Deallocate(&string); + parcBuffer_Release(&key); + parcBuffer_Release(&value); + parcHashMap_Release(&instance); +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcHashMap_Put); + LONGBOW_RUN_TEST_CASE(Global, parcHashMap_PutN); + LONGBOW_RUN_TEST_CASE(Global, parcHashMap_Put_Replace); + LONGBOW_RUN_TEST_CASE(Global, parcHashMap_Get_NoValue); + LONGBOW_RUN_TEST_CASE(Global, parcHashMap_Contains_True); + LONGBOW_RUN_TEST_CASE(Global, parcHashMap_Contains_False); + LONGBOW_RUN_TEST_CASE(Global, parcHashMap_Remove); + LONGBOW_RUN_TEST_CASE(Global, parcHashMap_Remove_False); + LONGBOW_RUN_TEST_CASE(Global, parcHashMap_Resize); + LONGBOW_RUN_TEST_CASE(Global, parcHashMap_GetClusteringNumber); + LONGBOW_RUN_TEST_CASE(Global, parcHashMap_CreateValueIterator); + LONGBOW_RUN_TEST_CASE(Global, parcHashMap_CreateValueIterator_HasNext); + LONGBOW_RUN_TEST_CASE(Global, parcHashMap_CreateValueIterator_Next); + LONGBOW_RUN_TEST_CASE(Global, parcHashMap_KeyIterator); + LONGBOW_RUN_TEST_CASE(Global, parcHashMap_KeyIterator_HasNext); + LONGBOW_RUN_TEST_CASE(Global, parcHashMap_KeyIterator_Next); + LONGBOW_RUN_TEST_CASE(Global, parcHashMap_KeyIterator_Remove); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) { + parcSafeMemory_ReportAllocation(1); + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcHashMap_Put) +{ + PARCHashMap *instance = parcHashMap_Create(); + + PARCBuffer *key = parcBuffer_WrapCString("key1"); + PARCBuffer *value = parcBuffer_WrapCString("value1"); + + //size_t keyReferences = parcObject_GetReferenceCount(key); + size_t valueReferences = parcObject_GetReferenceCount(value); + + parcHashMap_Put(instance, key, value); + //M.S. Put() now results in a copy of the key. + //assertTrue(keyReferences + 1 == parcObject_GetReferenceCount(key), "Expected key reference to be incremented by 1."); + assertTrue(valueReferences + 1 == parcObject_GetReferenceCount(value), "Expected value reference to be incremented by 1."); + + PARCBuffer *actual = (PARCBuffer *) parcHashMap_Get(instance, key); + + assertTrue(parcBuffer_Equals(value, actual), "Expected value was not returned from Get"); + + parcBuffer_Release(&key); + parcBuffer_Release(&value); + + parcHashMap_Release(&instance); +} + +typedef struct { + int64_t number; +} _Int; + +static char * +_int_toString(const _Int *anInt) +{ + char *result = parcMemory_AllocateAndClear(22); + sprintf(result, "%" PRIi64 "", anInt->number); + return result; +} + +static PARCHashCode +_int_hashCode(const _Int *anInt) +{ + PARCHashCode result = anInt->number; + + return result; +} + +parcObject_ExtendPARCObject(_Int, NULL, NULL, _int_toString, NULL, NULL, _int_hashCode, NULL); + +parcObject_ImplementRelease(_int, _Int); + +static _Int * +_int_Create(int64_t anInt) +{ + _Int *_int = parcObject_CreateInstance(_Int); + + if (_int != NULL) { + _int->number = anInt; + } + + return _int; +} + +LONGBOW_TEST_CASE(Global, parcHashMap_GetClusteringNumber) +{ + size_t minimumSize = 100; + PARCHashMap *instance = parcHashMap_CreateCapacity(minimumSize); + + double maxLoadFactor = instance->maxLoadFactor; + + // Load a hash map up to its load-factor + size_t testRunSize = minimumSize * maxLoadFactor - 20; + srand(time(NULL)); + for (int i = 0; i < testRunSize; ++i) { + _Int *key = _int_Create(rand()); + PARCBuffer *value = parcBuffer_Allocate(sizeof(uint32_t)); + parcBuffer_PutUint32(value, 1000 + i); + parcHashMap_Put(instance, key, value); + parcBuffer_Release(&value); + _int_Release(&key); + } + + double currentClusteringNumber = parcHashMap_GetClusteringNumber(instance); + + if (currentClusteringNumber < 0.5) { + testWarn("Oddly low clustering number detected."); + } + + if (currentClusteringNumber > 1.5) { + testWarn("Oddly high clustering number detected."); + } + + // This will load up one bucket + for (int i = 0; i < 20; ++i) { + _Int *key = _int_Create(1 + (100 * i)); + PARCBuffer *value = parcBuffer_Allocate(sizeof(uint32_t)); + parcBuffer_PutUint32(value, 10 + i); + parcHashMap_Put(instance, key, value); + parcBuffer_Release(&value); + _int_Release(&key); + } + + currentClusteringNumber = parcHashMap_GetClusteringNumber(instance); + + parcHashMap_Release(&instance); + + if (currentClusteringNumber < 2.9) { + testWarn("Oddly low clustering number detected."); + } +} + +LONGBOW_TEST_CASE(Global, parcHashMap_Resize) +{ + size_t initialSize = 8; + PARCHashMap *instance = parcHashMap_CreateCapacity(initialSize); + + PARCBuffer *key = parcBuffer_Allocate(sizeof(uint32_t)); + PARCBuffer *value42 = parcBuffer_WrapCString("value42"); + double maxLoadFactor = instance->maxLoadFactor; + + // Load a hash map up to its load-factor + size_t testRunSize = initialSize * maxLoadFactor; + for (uint32_t i = 0; i < testRunSize; ++i) { + parcBuffer_PutUint32(key, i); + PARCBuffer *value = parcBuffer_Allocate(sizeof(uint32_t)); + parcBuffer_PutUint32(value, 1000 + i); + parcHashMap_Put(instance, parcBuffer_Flip(key), value); + parcBuffer_Release(&value); + } + assertTrue(parcHashMap_Size(instance) == testRunSize, "Expect the size to be %zu", testRunSize); + assertTrue(instance->capacity == initialSize, "Expect to have the original capacity"); + + // Test for expected values + for (uint32_t i = 0; i < testRunSize; ++i) { + parcBuffer_PutUint32(key, i); + PARCBuffer *value = parcBuffer_Allocate(sizeof(uint32_t)); + parcBuffer_PutUint32(value, 1000 + i); + const PARCBuffer *storedValue = parcHashMap_Get(instance, parcBuffer_Flip(key)); + assertTrue(parcBuffer_Equals(value, storedValue), "Expect looked up values to match"); + parcBuffer_Release(&value); + } + + // Add one more item to the the hash map, this should trigger an expansion + parcBuffer_PutUint32(key, 42); + parcHashMap_Put(instance, parcBuffer_Flip(key), value42); + assertTrue(parcHashMap_Size(instance) == testRunSize + 1, "Expect the size to be %zu", testRunSize); + assertTrue(instance->capacity == 2 * initialSize, "Expect to have the original capacity"); + + // Re-test value look ups to make sure the new hash map still maps correctly + for (uint32_t i = 0; i < testRunSize; ++i) { + parcBuffer_PutUint32(key, i); + PARCBuffer *value = parcBuffer_Allocate(sizeof(uint32_t)); + parcBuffer_PutUint32(value, 1000 + i); + const PARCBuffer *storedValue = parcHashMap_Get(instance, parcBuffer_Flip(key)); + assertTrue(parcBuffer_Equals(value, storedValue), "Expect looked up values to match"); + parcBuffer_Release(&value); + } + double averageBucketSize = parcHashMap_GetClusteringNumber(instance); + parcBuffer_PutUint32(key, 42); + const PARCBuffer *storedValue = parcHashMap_Get(instance, parcBuffer_Flip(key)); + assertTrue(parcBuffer_Equals(value42, storedValue), "Expect to get back value42"); + parcBuffer_Release(&value42); + assertTrue(parcHashMap_GetClusteringNumber(instance) <= averageBucketSize, + "Expect the average bucket size to be less then it was"); + + // Now test multiple expansions to make sure they happened are result in a valid hash map + size_t testCapacity = 1024; + // If we load up to (maxLoadFactor * testCapacity) + 1, the capacity should expand to 2 * testCapacity + testRunSize = (testCapacity * maxLoadFactor) + 1; + for (uint32_t i = 0; i < testRunSize; ++i) { + parcBuffer_PutUint32(key, i); + PARCBuffer *value = parcBuffer_Allocate(sizeof(uint32_t)); + parcBuffer_PutUint32(value, 1000 + i); + parcHashMap_Put(instance, parcBuffer_Flip(key), value); + parcBuffer_Release(&value); + if (i == (testRunSize - 2)) { + averageBucketSize = parcHashMap_GetClusteringNumber(instance); + } + } + assertTrue(instance->capacity == (2 * testCapacity), + "Expect capacity to be %zu got %zu", (2 * testCapacity), instance->capacity); + assertTrue(parcHashMap_GetClusteringNumber(instance) < averageBucketSize, + "Expect the average bucket size to be less then it was"); + + // Now test multiple contractions. + // If we remove all elements from index "smallSize" (eg. 8) up we will be left with a map of size smallSize, + // the map capacity should be contracting and, because the minimum load factor is 0.25 and the contraction + // is a divide by 2, the last contraction should be from capacity of "smallSize * 4" (32) to one + // of "smallSize * 2" (16) when size goes from "smallSize +1" (9) to "smallSize" (8). + // + size_t smallSize = 8; + for (uint32_t i = smallSize; i < testRunSize; ++i) { + parcBuffer_PutUint32(key, i); + parcBuffer_Flip(key); + PARCBuffer *value = parcBuffer_Allocate(sizeof(uint32_t)); + parcBuffer_PutUint32(value, 1000 + i); + const PARCBuffer *storedValue = parcHashMap_Get(instance, key); + assertTrue(parcBuffer_Equals(value, storedValue), "Expect looked up values to match"); + parcBuffer_Release(&value); + + assertTrue(parcHashMap_Remove(instance, key), "Expect Remove to succeed"); + } + assertTrue(instance->size == smallSize, + "Expect the hash map to have size %zu, got %zu", smallSize, instance->size) + assertTrue(instance->capacity == (smallSize * 2), + "Expect capacity to be %zu, got %zu", (smallSize * 2), instance->capacity); + + // Re-test value look ups to make sure the new hash map still maps correctly + for (uint32_t i = 0; i < smallSize; ++i) { + parcBuffer_PutUint32(key, i); + PARCBuffer *value = parcBuffer_Allocate(sizeof(uint32_t)); + parcBuffer_PutUint32(value, 1000 + i); + const PARCBuffer *storedValue = parcHashMap_Get(instance, parcBuffer_Flip(key)); + assertTrue(parcBuffer_Equals(value, storedValue), "Expect looked up values to match"); + parcBuffer_Release(&value); + } + + parcBuffer_Release(&key); + parcHashMap_Release(&instance); +} + +LONGBOW_TEST_CASE(Global, parcHashMap_PutN) +{ + size_t testRunSize = 100; + + PARCHashMap *instance = parcHashMap_CreateCapacity(testRunSize); + + PARCBuffer *key = parcBuffer_Allocate(sizeof(uint32_t)); + PARCBuffer *value = parcBuffer_WrapCString("value1"); + PARCBuffer *value42 = parcBuffer_WrapCString("value42"); + for (uint32_t i = 0; i < testRunSize * 2; ++i) { + parcBuffer_PutUint32(key, i); + parcHashMap_Put(instance, parcBuffer_Flip(key), value); + if (i == 42) { + parcHashMap_Put(instance, key, value42); + } + } + + parcBuffer_PutUint32(key, 42); + PARCBuffer *actual = (PARCBuffer *) parcHashMap_Get(instance, parcBuffer_Flip(key)); + assertTrue(parcBuffer_Equals(value42, actual), "Expect to get back value42"); + + parcBuffer_Release(&key); + parcBuffer_Release(&value); + parcBuffer_Release(&value42); + parcHashMap_Release(&instance); +} + +LONGBOW_TEST_CASE(Global, parcHashMap_Put_Replace) +{ + PARCHashMap *instance = parcHashMap_Create(); + + PARCBuffer *key = parcBuffer_WrapCString("key1"); + PARCBuffer *value1 = parcBuffer_WrapCString("value1"); + PARCBuffer *value2 = parcBuffer_WrapCString("value2"); + + parcHashMap_Put(instance, key, value1); + + parcHashMap_Put(instance, key, value2); + + PARCBuffer *actual = (PARCBuffer *) parcHashMap_Get(instance, key); + + assertTrue(parcBuffer_Equals(value2, actual), "Expected value was not returned from Get"); + + parcBuffer_Release(&key); + parcBuffer_Release(&value1); + parcBuffer_Release(&value2); + parcHashMap_Release(&instance); +} + +LONGBOW_TEST_CASE(Global, parcHashMap_Get_NoValue) +{ + PARCHashMap *instance = parcHashMap_Create(); + + PARCBuffer *key = parcBuffer_WrapCString("key1"); + + PARCBuffer *actual = (PARCBuffer *) parcHashMap_Get(instance, key); + + assertNull(actual, "Expected parcHashMap_Get to return NULL for non-existent key."); + + parcBuffer_Release(&key); + + parcHashMap_Release(&instance); +} + +LONGBOW_TEST_CASE(Global, parcHashMap_Contains_True) +{ + PARCHashMap *instance = parcHashMap_Create(); + + PARCBuffer *key = parcBuffer_WrapCString("key1"); + PARCBuffer *value = parcBuffer_WrapCString("value1"); + + parcHashMap_Put(instance, key, value); + + bool actual = parcHashMap_Contains(instance, key); + + assertTrue(actual, "Expected parcHashMap_Contains to return true"); + + parcBuffer_Release(&key); + parcBuffer_Release(&value); + + parcHashMap_Release(&instance); +} + +LONGBOW_TEST_CASE(Global, parcHashMap_Contains_False) +{ + PARCHashMap *instance = parcHashMap_Create(); + + PARCBuffer *key = parcBuffer_WrapCString("key1"); + + bool actual = parcHashMap_Contains(instance, key); + + assertFalse(actual, "Expected parcHashMap_Contains to return NULL for non-existent key."); + + parcBuffer_Release(&key); + + parcHashMap_Release(&instance); +} + +LONGBOW_TEST_CASE(Global, parcHashMap_Remove) +{ + PARCHashMap *instance = parcHashMap_Create(); + + PARCBuffer *key = parcBuffer_WrapCString("key1"); + PARCBuffer *value = parcBuffer_WrapCString("value1"); + + parcHashMap_Put(instance, key, value); + + bool actual = parcHashMap_Remove(instance, key); + + assertTrue(actual, "Expected parcHashMap_Remove to return true."); + + parcBuffer_Release(&key); + parcBuffer_Release(&value); + + parcHashMap_Release(&instance); +} + +LONGBOW_TEST_CASE(Global, parcHashMap_Remove_False) +{ + PARCHashMap *instance = parcHashMap_Create(); + + PARCBuffer *key = parcBuffer_WrapCString("key1"); + PARCBuffer *key2 = parcBuffer_WrapCString("key2"); + PARCBuffer *value = parcBuffer_WrapCString("value1"); + + parcHashMap_Put(instance, key, value); + + bool actual = parcHashMap_Remove(instance, key2); + + assertFalse(actual, "Expected parcHashMap_Remove to return false."); + + parcBuffer_Release(&key); + parcBuffer_Release(&key2); + parcBuffer_Release(&value); + + parcHashMap_Release(&instance); +} + +LONGBOW_TEST_CASE(Global, parcHashMap_CreateValueIterator) +{ + PARCHashMap *instance = parcHashMap_Create(); + + PARCBuffer *key = parcBuffer_WrapCString("key1"); + PARCBuffer *value = parcBuffer_WrapCString("value1"); + + parcHashMap_Put(instance, key, value); + + PARCIterator *iterator = parcHashMap_CreateValueIterator(instance); + + assertNotNull(iterator, "Expected parcHashMap_ValueIterator to return non-null result"); + + parcIterator_Release(&iterator); + parcBuffer_Release(&key); + parcBuffer_Release(&value); + + parcHashMap_Release(&instance); +} + +LONGBOW_TEST_CASE(Global, parcHashMap_CreateValueIterator_HasNext) +{ + PARCHashMap *instance = parcHashMap_Create(); + + PARCBuffer *key = parcBuffer_WrapCString("key1"); + PARCBuffer *value = parcBuffer_WrapCString("value1"); + + parcHashMap_Put(instance, key, value); + + PARCIterator *iterator = parcHashMap_CreateValueIterator(instance); + + assertTrue(parcIterator_HasNext(iterator), "Expected parcIterator_HasNext to return true"); + + parcIterator_Release(&iterator); + parcBuffer_Release(&key); + parcBuffer_Release(&value); + + parcHashMap_Release(&instance); +} + +LONGBOW_TEST_CASE(Global, parcHashMap_CreateValueIterator_Next) +{ + PARCHashMap *instance = parcHashMap_Create(); + + PARCBuffer *key1 = parcBuffer_WrapCString("key1"); + PARCBuffer *value1 = parcBuffer_WrapCString("1"); + PARCBuffer *key2 = parcBuffer_WrapCString("key2"); + PARCBuffer *value2 = parcBuffer_WrapCString("2"); + PARCBuffer *key3 = parcBuffer_WrapCString("key3"); + PARCBuffer *value3 = parcBuffer_WrapCString("3"); + PARCBuffer *key4 = parcBuffer_WrapCString("key4"); + PARCBuffer *value4 = parcBuffer_WrapCString("4"); + + parcHashMap_Put(instance, key1, value1); + parcHashMap_Put(instance, key2, value2); + parcHashMap_Put(instance, key3, value3); + parcHashMap_Put(instance, key4, value4); + + PARCIterator *iterator = parcHashMap_CreateValueIterator(instance); + + while (parcIterator_HasNext(iterator)) { + PARCBuffer *actual = parcIterator_Next(iterator); + assertNotNull(actual, "Expected parcIterator_Next to return non-null"); + assertTrue(parcBuffer_Remaining(actual) > 0, "The same value appeared more than once in the iteration"); + parcBuffer_SetPosition(actual, parcBuffer_Limit(actual)); + } + parcIterator_Release(&iterator); + + parcBuffer_Release(&key1); + parcBuffer_Release(&value1); + parcBuffer_Release(&key2); + parcBuffer_Release(&value2); + parcBuffer_Release(&key3); + parcBuffer_Release(&value3); + parcBuffer_Release(&key4); + parcBuffer_Release(&value4); + + parcHashMap_Release(&instance); +} + +LONGBOW_TEST_CASE(Global, parcHashMap_KeyIterator) +{ + PARCHashMap *instance = parcHashMap_Create(); + + PARCBuffer *key = parcBuffer_WrapCString("key1"); + PARCBuffer *value = parcBuffer_WrapCString("value1"); + + parcHashMap_Put(instance, key, value); + + PARCIterator *iterator = parcHashMap_CreateKeyIterator(instance); + + assertNotNull(iterator, "Expected parcHashMap_KeyIterator to return non-null result"); + + parcIterator_Release(&iterator); + parcBuffer_Release(&key); + parcBuffer_Release(&value); + + parcHashMap_Release(&instance); +} + +LONGBOW_TEST_CASE(Global, parcHashMap_KeyIterator_HasNext) +{ + PARCHashMap *instance = parcHashMap_Create(); + + PARCBuffer *key = parcBuffer_WrapCString("key1"); + PARCBuffer *value = parcBuffer_WrapCString("value1"); + + parcHashMap_Put(instance, key, value); + + PARCIterator *iterator = parcHashMap_CreateKeyIterator(instance); + + assertTrue(parcIterator_HasNext(iterator), "Expected parcIterator_HasNext to return true"); + + parcIterator_Release(&iterator); + parcBuffer_Release(&key); + parcBuffer_Release(&value); + + parcHashMap_Release(&instance); +} + +LONGBOW_TEST_CASE(Global, parcHashMap_KeyIterator_Next) +{ + PARCHashMap *instance = parcHashMap_Create(); + + PARCBuffer *key1 = parcBuffer_WrapCString("key1"); + PARCBuffer *value1 = parcBuffer_WrapCString("1"); + PARCBuffer *key2 = parcBuffer_WrapCString("key2"); + PARCBuffer *value2 = parcBuffer_WrapCString("2"); + PARCBuffer *key3 = parcBuffer_WrapCString("key3"); + PARCBuffer *value3 = parcBuffer_WrapCString("3"); + PARCBuffer *key4 = parcBuffer_WrapCString("key4"); + PARCBuffer *value4 = parcBuffer_WrapCString("4"); + + parcHashMap_Put(instance, key1, value1); + parcHashMap_Put(instance, key2, value2); + parcHashMap_Put(instance, key3, value3); + parcHashMap_Put(instance, key4, value4); + + PARCIterator *iterator = parcHashMap_CreateKeyIterator(instance); + + while (parcIterator_HasNext(iterator)) { + PARCBuffer *actual = parcIterator_Next(iterator); + assertNotNull(actual, "Expected parcIterator_Next to return non-null"); + assertTrue(parcBuffer_Remaining(actual) > 0, "The same value appeared more than once in the iteration"); + parcBuffer_SetPosition(actual, parcBuffer_Limit(actual)); + } + parcIterator_Release(&iterator); + + parcBuffer_Release(&key1); + parcBuffer_Release(&value1); + parcBuffer_Release(&key2); + parcBuffer_Release(&value2); + parcBuffer_Release(&key3); + parcBuffer_Release(&value3); + parcBuffer_Release(&key4); + parcBuffer_Release(&value4); + + parcHashMap_Release(&instance); +} + +LONGBOW_TEST_CASE(Global, parcHashMap_KeyIterator_Remove) +{ + PARCHashMap *instance = parcHashMap_Create(); + + PARCBuffer *key1 = parcBuffer_WrapCString("key1"); + PARCBuffer *value1 = parcBuffer_WrapCString("1"); + PARCBuffer *key2 = parcBuffer_WrapCString("key2"); + PARCBuffer *value2 = parcBuffer_WrapCString("2"); + PARCBuffer *key3 = parcBuffer_WrapCString("key3"); + PARCBuffer *value3 = parcBuffer_WrapCString("3"); + PARCBuffer *key4 = parcBuffer_WrapCString("key4"); + PARCBuffer *value4 = parcBuffer_WrapCString("4"); + + parcHashMap_Put(instance, key1, value1); + parcHashMap_Put(instance, key2, value2); + parcHashMap_Put(instance, key3, value3); + parcHashMap_Put(instance, key4, value4); + + assertTrue(parcHashMap_Size(instance) == 4, "Expected 4, actual %zd", parcHashMap_Size(instance)); + PARCIterator *iterator = parcHashMap_CreateKeyIterator(instance); + + while (parcIterator_HasNext(iterator)) { + PARCBuffer *key = parcBuffer_Acquire(parcIterator_Next(iterator)); + parcIterator_Remove(iterator); + assertNull(parcHashMap_Get(instance, key), "Expected deleted entry to not be gettable."); + parcBuffer_Release(&key); + } + parcIterator_Release(&iterator); + + assertTrue(parcHashMap_Size(instance) == 0, "Expected 0, actual %zd", parcHashMap_Size(instance)); + parcBuffer_Release(&key1); + parcBuffer_Release(&value1); + parcBuffer_Release(&key2); + parcBuffer_Release(&value2); + parcBuffer_Release(&key3); + parcBuffer_Release(&value3); + parcBuffer_Release(&key4); + parcBuffer_Release(&value4); + + parcHashMap_Release(&instance); +} + +LONGBOW_TEST_FIXTURE(Static) +{ + LONGBOW_RUN_TEST_CASE(Static, parcHashMapEntry); +} + +LONGBOW_TEST_FIXTURE_SETUP(Static) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Static) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) { + parcSafeMemory_ReportAllocation(1); + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Static, parcHashMapEntry) +{ + PARCBuffer *key = parcBuffer_WrapCString("key1"); + PARCBuffer *value = parcBuffer_WrapCString("value1"); + + _PARCHashMapEntry *instance = _parcHashMapEntry_Create(key, value); + parcBuffer_Release(&key); + parcBuffer_Release(&value); + + _parcHashMapEntry_Release(&instance); +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_HashMap); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_InputStream.c b/libparc/parc/algol/test/test_parc_InputStream.c new file mode 100755 index 00000000..46d47862 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_InputStream.c @@ -0,0 +1,109 @@ +/* + * 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 the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../parc_InputStream.c" + +#include <LongBow/unit-test.h> + +LONGBOW_TEST_RUNNER(parc_InputStream) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_InputStream) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_InputStream) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcInputStream); + LONGBOW_RUN_TEST_CASE(Global, parcInputStream_Acquire); + LONGBOW_RUN_TEST_CASE(Global, parcInputStream_Read); + LONGBOW_RUN_TEST_CASE(Global, parcInputStream_Release); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcInputStream) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_CASE(Global, parcInputStream_Acquire) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_CASE(Global, parcInputStream_Read) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_CASE(Global, parcInputStream_Release) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_FIXTURE(Local) +{ + LONGBOW_RUN_TEST_CASE(Local, parcInputStream_Finalize); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Local, parcInputStream_Finalize) +{ + testUnimplemented(""); +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_InputStream); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_Iterator.c b/libparc/parc/algol/test/test_parc_Iterator.c new file mode 100644 index 00000000..d5778721 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_Iterator.c @@ -0,0 +1,192 @@ +/* + * 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 the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../parc_Iterator.c" + +#include <inttypes.h> +#include <stdio.h> + +#include <parc/algol/parc_Buffer.h> +#include <parc/algol/parc_SafeMemory.h> +#include <parc/testing/parc_ObjectTesting.h> +#include <parc/testing/parc_MemoryTesting.h> + +#include <LongBow/unit-test.h> + +LONGBOW_TEST_RUNNER(parc_Iterator) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(CreateAcquireRelease); + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_Iterator) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_Iterator) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(CreateAcquireRelease) +{ + LONGBOW_RUN_TEST_CASE(CreateAcquireRelease, parcIterator_CreateAcquireRelease); +} + +LONGBOW_TEST_FIXTURE_SETUP(CreateAcquireRelease) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(CreateAcquireRelease) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s", longBowTestCase_GetName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +static uint64_t _state; + +static void * +init(PARCObject *object __attribute__((unused))) +{ + _state = 0; + return &_state; +} + +static bool +hasNext(PARCObject *object __attribute__((unused)), void *state) +{ + uint64_t *value = (uint64_t *) state; + return (*value < 5); +} + +static void * +next(PARCObject *object __attribute__((unused)), void *state) +{ + uint64_t *value = (uint64_t *) state; + + (*value)++; + return state; +} + +static void +removex(PARCObject *object __attribute__((unused)), void **state) +{ +} + +static void * +getElement(PARCObject *object __attribute__((unused)), void *state) +{ + uint64_t *value = (uint64_t *) state; + return (void *) *value; +} + +static void +fini(PARCObject *object __attribute__((unused)), void *state __attribute__((unused))) +{ +} + +static void +assertValid(const void *state __attribute__((unused))) +{ +} + +LONGBOW_TEST_CASE(CreateAcquireRelease, parcIterator_CreateAcquireRelease) +{ + PARCBuffer *buffer = parcBuffer_Allocate(1); + + PARCIterator *iterator = parcIterator_Create(buffer, init, hasNext, next, removex, getElement, fini, assertValid); + + parcObjectTesting_AssertAcquireReleaseContract(parcIterator_Acquire, iterator); + parcBuffer_Release(&buffer); + parcIterator_Release(&iterator); +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcIterator_HasNext); + LONGBOW_RUN_TEST_CASE(Global, parcIterator_Next); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + bool leaked = parcMemoryTesting_ExpectedOutstanding(0, "%s leaks memory \n", longBowTestCase_GetName(testCase)) != true; + if (leaked) { + parcSafeMemory_ReportAllocation(STDOUT_FILENO); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcIterator_HasNext) +{ + PARCBuffer *buffer = parcBuffer_Allocate(1); + + PARCIterator *iterator = parcIterator_Create(buffer, init, hasNext, next, removex, getElement, fini, assertValid); + + while (parcIterator_HasNext(iterator)) { + uint64_t value = (uint64_t) parcIterator_Next(iterator); + printf("%" PRIu64 "\n", value); + } + parcBuffer_Release(&buffer); + parcIterator_Release(&iterator); +} + +LONGBOW_TEST_CASE(Global, parcIterator_Next) +{ + PARCBuffer *buffer = parcBuffer_Allocate(1); + + PARCIterator *iterator = parcIterator_Create(buffer, init, hasNext, next, removex, getElement, fini, assertValid); + + while (parcIterator_HasNext(iterator)) { + uint64_t value = (uint64_t) parcIterator_Next(iterator); + printf("%" PRIu64 "\n", value); + } + parcBuffer_Release(&buffer); + parcIterator_Release(&iterator); +} + +LONGBOW_TEST_CASE(Local, _finalize) +{ + testUnimplemented(""); +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_Iterator); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_JSON.c b/libparc/parc/algol/test/test_parc_JSON.c new file mode 100644 index 00000000..d1825319 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_JSON.c @@ -0,0 +1,690 @@ +/* + * 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 "../parc_JSON.c" +#include "../parc_JSONPair.c" + +#include <stdio.h> +#include <fcntl.h> +#include <inttypes.h> + +#include <LongBow/unit-test.h> + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. + + +#include "../parc_List.h" +#include "../parc_ArrayList.h" +#include "../parc_SafeMemory.h" +#include "../parc_Memory.h" +#include <parc/testing/parc_ObjectTesting.h> + +LONGBOW_TEST_RUNNER(parc_JSON) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Static); + LONGBOW_RUN_TEST_FIXTURE(JSON); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_JSON) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_JSON) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(JSON) +{ + LONGBOW_RUN_TEST_CASE(JSON, parcJSON_CreateRelease); + LONGBOW_RUN_TEST_CASE(JSON, parcJSON_Equals); + LONGBOW_RUN_TEST_CASE(JSON, parcJSON_HashCode); + LONGBOW_RUN_TEST_CASE(JSON, parcJSON_Copy); + LONGBOW_RUN_TEST_CASE(JSON, parcJSON_Add); + LONGBOW_RUN_TEST_CASE(JSON, parcJSON_GetMembers); + LONGBOW_RUN_TEST_CASE(JSON, parcJSON_GetPairByName); + LONGBOW_RUN_TEST_CASE(JSON, parcJSON_GetValueByName); + LONGBOW_RUN_TEST_CASE(JSON, parcJSON_GetPairByIndex); + LONGBOW_RUN_TEST_CASE(JSON, parcJSON_GetValueByIndex); + LONGBOW_RUN_TEST_CASE(JSON, parcJSON_BuildString); + LONGBOW_RUN_TEST_CASE(JSON, parcJSON_ToString); + LONGBOW_RUN_TEST_CASE(JSON, parcJSON_ToCompactString); + LONGBOW_RUN_TEST_CASE(JSON, parcJSON_GetByPath); + LONGBOW_RUN_TEST_CASE(JSON, parcJSON_GetByPath_BadArrayIndex); + LONGBOW_RUN_TEST_CASE(JSON, parcJSON_GetByPath_DeadEndPath); + LONGBOW_RUN_TEST_CASE(JSON, parcJSON_ParseString); + LONGBOW_RUN_TEST_CASE(JSON, parcJSON_ParseBuffer_WithExcess); + LONGBOW_RUN_TEST_CASE(JSON, parcJSON_Display); + LONGBOW_RUN_TEST_CASE(JSON, parcJSON_AddString); + LONGBOW_RUN_TEST_CASE(JSON, parcJSON_AddObject); + LONGBOW_RUN_TEST_CASE(JSON, parcJSON_AddInteger); + LONGBOW_RUN_TEST_CASE(JSON, parcJSON_AddBoolean); + LONGBOW_RUN_TEST_CASE(JSON, parcJSON_AddArray); + LONGBOW_RUN_TEST_CASE(JSON, parcJSON_AddValue); +} + +typedef struct { + PARCJSON *json; + char *expected; + char *compactExpected; +} TestData; + +LONGBOW_TEST_FIXTURE_SETUP(JSON) +{ + TestData *data = parcMemory_Allocate(sizeof(TestData)); + + char *temp = "{ \"string\" : \"foo\\/bar\", \"null\" : null, \"true\" : true, \"false\" : false, \"integer\" : 31415, \"float\" : 3.141500, \"json\" : { \"string\" : \"foo\\/bar\" }, \"array\" : [ null, false, true, 31415, \"string\", [ null, false, true, 31415, \"string\" ], { } ] }"; + data->expected = parcMemory_StringDuplicate(temp, strlen(temp)); + + temp = "{\"string\":\"foo/bar\",\"null\":null,\"true\":true,\"false\":false,\"integer\":31415,\"float\":3.141500,\"json\":{\"string\":\"foo/bar\"},\"array\":[null,false,true,31415,\"string\",[null,false,true,31415,\"string\"],{}]}"; + data->compactExpected = parcMemory_StringDuplicate(temp, strlen(temp)); + + data->json = parcJSON_ParseString(temp); + + longBowTestCase_SetClipBoardData(testCase, data); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(JSON) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + parcJSON_Release(&data->json); + parcMemory_Deallocate(&data->expected); + parcMemory_Deallocate(&data->compactExpected); + + parcMemory_Deallocate(&data); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("Errors %s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + + +LONGBOW_TEST_CASE(JSON, parcJSON_CreateRelease) +{ + PARCJSON *json = parcJSON_Create(); + + parcJSON_Release(&json); + assertNull(json, "Expected the NULL pointer side-effect of Release."); +} + +LONGBOW_TEST_CASE(JSON, parcJSON_Copy) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCJSON *copy = parcJSON_Copy(data->json); + + assertTrue(parcJSON_Equals(data->json, copy), "Expect copy to equal original"); + + parcJSON_Release(©); +} + +LONGBOW_TEST_CASE(JSON, parcJSON_HashCode) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCHashCode expected = parcHashCode_Hash((uint8_t *) data->compactExpected, strlen(data->compactExpected)); + + PARCHashCode hashCode = parcJSON_HashCode(data->json); + + assertTrue(hashCode == expected, "Expect correct hash code"); +} + +LONGBOW_TEST_CASE(JSON, parcJSON_Add) +{ + PARCJSON *json = parcJSON_Create(); + { + PARCBuffer *string = parcBuffer_WrapCString("string"); + PARCJSONValue *stringValue = parcJSONValue_CreateFromString(string); + PARCBuffer *stringName = parcBuffer_WrapCString("string"); + PARCJSONPair *pair = parcJSONPair_Create(stringName, stringValue); + parcJSON_AddPair(json, pair); + parcJSONPair_Release(&pair); + parcBuffer_Release(&stringName); + parcJSONValue_Release(&stringValue); + parcBuffer_Release(&string); + } + { + PARCBuffer *name = parcBuffer_WrapCString("null"); + PARCJSONValue *value = parcJSONValue_CreateFromNULL(); + PARCJSONPair *pair = parcJSONPair_Create(name, value); + parcJSON_AddPair(json, pair); + parcJSONPair_Release(&pair); + parcBuffer_Release(&name); + parcJSONValue_Release(&value); + } + { + PARCBuffer *name = parcBuffer_WrapCString("true"); + PARCJSONValue *value = parcJSONValue_CreateFromBoolean(true); + PARCJSONPair *pair = parcJSONPair_Create(name, value); + parcJSON_AddPair(json, pair); + parcJSONPair_Release(&pair); + parcBuffer_Release(&name); + parcJSONValue_Release(&value); + } + { + PARCBuffer *name = parcBuffer_WrapCString("false"); + PARCJSONValue *value = parcJSONValue_CreateFromBoolean(false); + PARCJSONPair *pair = parcJSONPair_Create(name, value); + parcJSON_AddPair(json, pair); + parcJSONPair_Release(&pair); + parcBuffer_Release(&name); + parcJSONValue_Release(&value); + } + { + PARCBuffer *name = parcBuffer_WrapCString("integer"); + PARCJSONValue *value = parcJSONValue_CreateFromInteger(31415); + PARCJSONPair *pair = parcJSONPair_Create(name, value); + parcJSON_AddPair(json, pair); + parcJSONPair_Release(&pair); + parcBuffer_Release(&name); + parcJSONValue_Release(&value); + } + { + PARCBuffer *name = parcBuffer_WrapCString("float"); + PARCJSONValue *value = parcJSONValue_CreateFromFloat(3.1415); + PARCJSONPair *pair = parcJSONPair_Create(name, value); + parcJSON_AddPair(json, pair); + parcJSONPair_Release(&pair); + parcBuffer_Release(&name); + parcJSONValue_Release(&value); + } + + parcJSON_Release(&json); +} + +LONGBOW_TEST_CASE(JSON, parcJSON_GetMembers) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + char *s = parcJSON_ToString(data->json); + parcMemory_Deallocate((void **) &s); + + PARCList *members = parcJSON_GetMembers(data->json); + assertTrue(parcList_Size(members) == 8, "Expected 8, actual %zd", parcList_Size(members)); +} + +LONGBOW_TEST_CASE(JSON, parcJSON_GetPairByName) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + int expected = 31415; + const PARCJSONPair *pair = parcJSON_GetPairByName(data->json, "integer"); + + PARCBuffer *name = parcJSONPair_GetName(pair); + PARCJSONValue *value = parcJSONPair_GetValue(pair); + + int64_t actual = parcJSONValue_GetInteger(value); + + PARCBuffer *expectedName = parcBuffer_WrapCString("integer"); + + assertTrue(parcBuffer_Equals(expectedName, name), + "Expected 'integer', actual '%s'", (char *) parcBuffer_ToString(name)); + + assertTrue(expected == actual, "Expected %d, actual %" PRIi64 "", expected, actual); + + parcBuffer_Release(&expectedName); +} + +LONGBOW_TEST_CASE(JSON, parcJSON_GetValueByName) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + int expected = 31415; + const PARCJSONValue *value = parcJSON_GetValueByName(data->json, "integer"); + + int64_t actual = parcJSONValue_GetInteger(value); + + PARCBuffer *expectedName = parcBuffer_WrapCString("integer"); + + assertTrue(expected == actual, "Expected %d, actual %" PRIi64 "", expected, actual); + + parcBuffer_Release(&expectedName); +} + +LONGBOW_TEST_CASE(JSON, parcJSON_GetPairByIndex) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCJSONPair *pair = parcJSON_GetPairByIndex(data->json, 0); + PARCBuffer *name = parcJSONPair_GetName(pair); + PARCBuffer *expectedName = parcBuffer_WrapCString("string"); + assertTrue(parcBuffer_Equals(expectedName, name), + "Expected 'string', actual '%s'", (char *) parcBuffer_ToString(name)); + parcBuffer_Release(&expectedName); + + pair = parcJSON_GetPairByIndex(data->json, 1); + name = parcJSONPair_GetName(pair); + expectedName = parcBuffer_WrapCString("null"); + assertTrue(parcBuffer_Equals(expectedName, name), + "Expected 'null', actual '%s'", (char *) parcBuffer_ToString(name)); + parcBuffer_Release(&expectedName); + + pair = parcJSON_GetPairByIndex(data->json, 2); + name = parcJSONPair_GetName(pair); + expectedName = parcBuffer_WrapCString("true"); + assertTrue(parcBuffer_Equals(expectedName, name), + "Expected 'true', actual '%s'", (char *) parcBuffer_ToString(name)); + parcBuffer_Release(&expectedName); + + pair = parcJSON_GetPairByIndex(data->json, 3); + name = parcJSONPair_GetName(pair); + expectedName = parcBuffer_WrapCString("false"); + assertTrue(parcBuffer_Equals(expectedName, name), + "Expected 'false', actual '%s'", (char *) parcBuffer_ToString(name)); + parcBuffer_Release(&expectedName); + + pair = parcJSON_GetPairByIndex(data->json, 4); + name = parcJSONPair_GetName(pair); + expectedName = parcBuffer_WrapCString("integer"); + assertTrue(parcBuffer_Equals(expectedName, name), + "Expected 'integer', actual '%s'", (char *) parcBuffer_ToString(name)); + parcBuffer_Release(&expectedName); + + pair = parcJSON_GetPairByIndex(data->json, 5); + name = parcJSONPair_GetName(pair); + expectedName = parcBuffer_WrapCString("float"); + assertTrue(parcBuffer_Equals(expectedName, name), + "Expected 'float', actual '%s'", (char *) parcBuffer_ToString(name)); + parcBuffer_Release(&expectedName); + + pair = parcJSON_GetPairByIndex(data->json, 6); + name = parcJSONPair_GetName(pair); + expectedName = parcBuffer_WrapCString("json"); + assertTrue(parcBuffer_Equals(expectedName, name), + "Expected 'json', actual '%s'", (char *) parcBuffer_ToString(name)); + parcBuffer_Release(&expectedName); + + pair = parcJSON_GetPairByIndex(data->json, 7); + name = parcJSONPair_GetName(pair); + expectedName = parcBuffer_WrapCString("array"); + assertTrue(parcBuffer_Equals(expectedName, name), + "Expected 'array', actual '%s'", (char *) parcBuffer_ToString(name)); + parcBuffer_Release(&expectedName); +} + +LONGBOW_TEST_CASE(JSON, parcJSON_GetValueByIndex) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCJSONValue *value = parcJSON_GetValueByIndex(data->json, 0); + assertTrue(parcJSONValue_IsString(value), + "Expected value to be type string"); + + value = parcJSON_GetValueByIndex(data->json, 1); + assertTrue(parcJSONValue_IsNull(value), + "Expected value to be type string"); + + value = parcJSON_GetValueByIndex(data->json, 2); + assertTrue(parcJSONValue_IsBoolean(value), + "Expected value to be type string"); + + value = parcJSON_GetValueByIndex(data->json, 3); + assertTrue(parcJSONValue_IsBoolean(value), + "Expected value to be type string"); + + value = parcJSON_GetValueByIndex(data->json, 4); + assertTrue(parcJSONValue_IsNumber(value), + "Expected value to be type string"); + + value = parcJSON_GetValueByIndex(data->json, 5); + assertTrue(parcJSONValue_IsNumber(value), + "Expected value to be type string"); + + value = parcJSON_GetValueByIndex(data->json, 6); + assertTrue(parcJSONValue_IsJSON(value), + "Expected value to be type string"); + + value = parcJSON_GetValueByIndex(data->json, 7); + assertTrue(parcJSONValue_IsArray(value), + "Expected value to be type string"); +} + +LONGBOW_TEST_CASE(JSON, parcJSON_BuildString) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCBufferComposer *composer = parcBufferComposer_Create(); + parcJSON_BuildString(data->json, composer, false); + + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + char *actual = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + parcBufferComposer_Release(&composer); + + assertTrue(strcmp(data->expected, actual) == 0, "Expected %s, actual %s", data->expected, actual); + parcMemory_Deallocate((void **) &actual); + + composer = parcBufferComposer_Create(); + parcJSON_BuildString(data->json, composer, true); + + tempBuffer = parcBufferComposer_ProduceBuffer(composer); + actual = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + parcBufferComposer_Release(&composer); + + assertTrue(strcmp(data->compactExpected, actual) == 0, "Expected %s, actual %s", data->compactExpected, actual); + parcMemory_Deallocate((void **) &actual); +} + +LONGBOW_TEST_CASE(JSON, parcJSON_ToString) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + char *actual = parcJSON_ToString(data->json); + assertTrue(strcmp(data->expected, actual) == 0, "Expected %s, actual %s", data->expected, actual); + + parcMemory_Deallocate((void **) &actual); +} + +LONGBOW_TEST_CASE(JSON, parcJSON_ToCompactString) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + char *actual = parcJSON_ToCompactString(data->json); + assertTrue(strcmp(data->compactExpected, actual) == 0, "Expected %s, actual %s", data->expected, actual); + + parcMemory_Deallocate((void **) &actual); +} + +LONGBOW_TEST_CASE(JSON, parcJSON_GetByPath) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCJSON *json = data->json; + + char *s = parcJSON_ToString(json); + printf("%s\n", s); + parcMemory_Deallocate((void **) &s); + + const PARCJSONValue *value = parcJSON_GetByPath(json, "/string"); + assertTrue(parcJSONValue_IsString(value), "Expected /string to be a string type."); + value = parcJSON_GetByPath(json, "/null"); + assertTrue(parcJSONValue_IsNull(value), "Expected /null to be a null type."); + value = parcJSON_GetByPath(json, "/true"); + assertTrue(parcJSONValue_IsBoolean(value), "Expected /true to be a boolean type."); + value = parcJSON_GetByPath(json, "/integer"); + assertTrue(parcJSONValue_IsNumber(value), "Expected /integer to be a number type."); + value = parcJSON_GetByPath(json, "/float"); + assertTrue(parcJSONValue_IsNumber(value), "Expected /float to be a number type."); + value = parcJSON_GetByPath(json, "/array"); + assertTrue(parcJSONValue_IsArray(value), "Expected /array to be an array type."); + value = parcJSON_GetByPath(json, "/nonexistent"); + assertNull(value, "Expected /nonexistent to be NULL"); + + value = parcJSON_GetByPath(json, "/array/1"); + assertTrue(parcJSONValue_IsBoolean(value), "Expected /array/0 to be a boolean type."); + + value = parcJSON_GetByPath(json, "/array/5"); + assertTrue(parcJSONValue_IsArray(value), "Expected /array/5 to be an array type."); + + assertNotNull(value, "Expected non-null pair"); +} + +LONGBOW_TEST_CASE(JSON, parcJSON_GetByPath_BadArrayIndex) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + const PARCJSONValue *value = parcJSON_GetByPath(data->json, "/array/100"); + assertNull(value, "Expected null value return from parcJSON_GetByPath"); +} + +LONGBOW_TEST_CASE(JSON, parcJSON_GetByPath_DeadEndPath) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + const PARCJSONValue *value = parcJSON_GetByPath(data->json, "/string/foo"); + assertNull(value, "Expected null value return from parcJSON_GetByPath"); +} + +LONGBOW_TEST_CASE(JSON, parcJSON_Equals) +{ + PARCJSON *x = parcJSON_ParseString("{ \"string\" : \"xyzzy\" }"); + PARCJSON *y = parcJSON_ParseString("{ \"string\" : \"xyzzy\" }"); + PARCJSON *z = parcJSON_ParseString("{ \"string\" : \"xyzzy\" }"); + + PARCJSON *notEqual1 = parcJSON_ParseString("{ \"string\" : \"string\" }"); + + PARCJSON *notEqual2 = parcJSON_ParseString("{ \"string\" : \"xyzzy\", \"integer\" : 1 }"); + + parcObjectTesting_AssertEqualsFunction(parcJSON_Equals, x, y, z, notEqual1, notEqual2); + + parcJSON_Release(&x); + parcJSON_Release(&y); + parcJSON_Release(&z); + parcJSON_Release(¬Equal1); + parcJSON_Release(¬Equal2); +} + +LONGBOW_TEST_CASE(JSON, parcJSON_Display) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + parcJSON_Display(data->json, 0); +} + +LONGBOW_TEST_CASE(JSON, parcJSON_ParseString) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCJSON *json = parcJSON_ParseString(data->expected); + + char *actual = parcJSON_ToString(json); + + assertTrue(strcmp(data->expected, actual) == 0, "Expected %s, actual %s", data->expected, actual); + + parcMemory_Deallocate((void **) &actual); + + parcJSON_Release(&json); +} + +LONGBOW_TEST_CASE(JSON, parcJSON_ParseBuffer_WithExcess) +{ + char *string = "{ \"string\" : \"string\", \"null\" : null, \"true\" : true, \"false\" : false, \"integer\" : 31415, \"float\" : 3.141500, \"array\" : [ null, false, true, 31415, \"string\", [ null, false, true, 31415, \"string\" ], { } ] }Xhowdy"; + PARCBuffer *buffer = parcBuffer_WrapCString((char *) string); + + PARCJSON *json = parcJSON_ParseBuffer(buffer); + + char actual = parcBuffer_GetUint8(buffer); + assertTrue(actual == 'X', "Expected buffer position to point to X, actual %x", actual); + + parcBuffer_Release(&buffer); + parcJSON_Release(&json); +} + +LONGBOW_TEST_CASE(JSON, parcJSON_AddString) +{ + PARCJSON *json = parcJSON_Create(); + + char *expectedName = "string"; + char *expectedValue = "value"; + + parcJSON_AddString(json, expectedName, expectedValue); + + const PARCJSONPair *pair = parcJSON_GetPairByName(json, "string"); + PARCBuffer *actualName = parcJSONPair_GetName(pair); + PARCJSONValue *actualValue = parcJSONPair_GetValue(pair); + + assertTrue(strcmp(expectedName, parcBuffer_Overlay(actualName, 0)) == 0, + "Expected name %s, actual %s", + expectedName, + parcBuffer_ToString(actualName)); + assertTrue(strcmp(expectedValue, parcBuffer_Overlay(parcJSONValue_GetString(actualValue), 0)) == 0, + "Expected value %s, actual %s", + expectedValue, + parcJSONValue_ToString(actualValue)); + + parcJSON_Release(&json); +} + +LONGBOW_TEST_CASE(JSON, parcJSON_AddObject) +{ + PARCJSON *json = parcJSON_Create(); + + PARCJSON *expectedValue = parcJSON_ParseString("{ \"string\" : \"xyzzy\" }"); + parcJSON_AddObject(json, "object", expectedValue); + + char *expectedName = "object"; + const PARCJSONPair *pair = parcJSON_GetPairByName(json, expectedName); + + PARCBuffer *actualName = parcJSONPair_GetName(pair); + PARCJSONValue *actualValue = parcJSONPair_GetValue(pair); + + assertTrue(strcmp(expectedName, parcBuffer_Overlay(actualName, 0)) == 0, + "Expected name %s, actual %s", expectedName, (char *) parcBuffer_ToString(actualName)); + + assertTrue(parcJSON_Equals(expectedValue, parcJSONValue_GetJSON(actualValue)), + "Expected value did not match the actual value."); + + parcJSON_Release(&expectedValue); + parcJSON_Release(&json); +} + +LONGBOW_TEST_CASE(JSON, parcJSON_AddInteger) +{ + PARCJSON *json = parcJSON_Create(); + + char *expectedName = "integer"; + uint64_t expectedValue = 12345; + + parcJSON_AddInteger(json, expectedName, expectedValue); + + const PARCJSONPair *pair = parcJSON_GetPairByName(json, expectedName); + + PARCBuffer *actualName = parcJSONPair_GetName(pair); + PARCJSONValue *actualValue = parcJSONPair_GetValue(pair); + + assertTrue(strcmp(expectedName, parcBuffer_Overlay(actualName, 0)) == 0, + "Expected name %s, actual %s", expectedName, (char *) parcBuffer_ToString(actualName)); + + assertTrue(expectedValue == parcJSONValue_GetInteger(actualValue), + "Expected %" PRIi64 "d actual %" PRIi64 "d", expectedValue, parcJSONValue_GetInteger(actualValue)); + + parcJSON_Release(&json); +} + +LONGBOW_TEST_CASE(JSON, parcJSON_AddBoolean) +{ + PARCJSON *json = parcJSON_Create(); + + char *expectedName = "boolean"; + bool expectedValue = true; + + parcJSON_AddBoolean(json, expectedName, expectedValue); + + const PARCJSONPair *pair = parcJSON_GetPairByName(json, expectedName); + + PARCBuffer *actualName = parcJSONPair_GetName(pair); + PARCJSONValue *actualValue = parcJSONPair_GetValue(pair); + + assertTrue(strcmp(expectedName, parcBuffer_Overlay(actualName, 0)) == 0, + "Expected name %s, actual %s", expectedName, (char *) parcBuffer_ToString(actualName)); + + assertTrue(expectedValue == parcJSONValue_GetBoolean(actualValue), + "Expected %d actual %d", expectedValue, parcJSONValue_GetBoolean(actualValue)); + + parcJSON_Release(&json); +} + +LONGBOW_TEST_CASE(JSON, parcJSON_AddArray) +{ + PARCJSON *json = parcJSON_Create(); + + char *expectedName = "array"; + + PARCJSONArray *array = parcJSONArray_Create(); + PARCJSONValue *value = parcJSONValue_CreateFromCString("Some Pig"); + parcJSONArray_AddValue(array, value); + + parcJSON_AddArray(json, expectedName, array); + parcJSONArray_Release(&array); + + const PARCJSONPair *pair = parcJSON_GetPairByName(json, expectedName); + + PARCBuffer *actualName = parcJSONPair_GetName(pair); + PARCJSONValue *actualValue = parcJSONPair_GetValue(pair); + assertTrue(strcmp(expectedName, parcBuffer_Overlay(actualName, 0)) == 0, + "Expected name %s, actual %s", expectedName, (char *) parcBuffer_ToString(actualName)); + assertTrue(parcJSONValue_IsArray(actualValue), "Expect value to be type PARCJSONArray"); + array = parcJSONValue_GetArray(actualValue); + PARCJSONValue *result = parcJSONArray_GetValue(array, 0); + assertTrue(parcBuffer_Equals(parcJSONValue_GetString(result), parcJSONValue_GetString(value)), + "Expected %s actual %s", + parcJSONValue_ToString(value), + parcJSONValue_ToString(result)); + + parcJSONValue_Release(&value); + + parcJSON_Release(&json); +} + +LONGBOW_TEST_CASE(JSON, parcJSON_AddValue) +{ + PARCJSON *json = parcJSON_Create(); + + char *expectedName = "value"; + + PARCJSONValue *value = parcJSONValue_CreateFromCString("Some Pig"); + + parcJSON_AddValue(json, expectedName, value); + + const PARCJSONPair *pair = parcJSON_GetPairByName(json, expectedName); + + PARCBuffer *actualName = parcJSONPair_GetName(pair); + PARCJSONValue *actualValue = parcJSONPair_GetValue(pair); + assertTrue(strcmp(expectedName, parcBuffer_Overlay(actualName, 0)) == 0, + "Expected name %s, actual %s", expectedName, (char *) parcBuffer_ToString(actualName)); + assertTrue(parcJSONValue_IsString(actualValue), "Expect value to be type PARCJSONArray"); + assertTrue(parcBuffer_Equals(parcJSONValue_GetString(actualValue), parcJSONValue_GetString(value)), + "Expected %s actual %s", + parcJSONValue_ToString(value), + parcJSONValue_ToString(actualValue)); + + parcJSONValue_Release(&value); + + parcJSON_Release(&json); +} + +LONGBOW_TEST_FIXTURE(Static) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Static) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Static) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_JSON); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_JSONArray.c b/libparc/parc/algol/test/test_parc_JSONArray.c new file mode 100755 index 00000000..d0ad9ec5 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_JSONArray.c @@ -0,0 +1,227 @@ +/* + * 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 "../parc_JSONArray.c" + +#include <LongBow/unit-test.h> +#include <stdio.h> + +#include "../parc_List.h" +#include "../parc_ArrayList.h" +#include "../parc_SafeMemory.h" +#include "../parc_Memory.h" +#include <parc/testing/parc_ObjectTesting.h> + +LONGBOW_TEST_RUNNER(parc_JSONArray) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Static); + LONGBOW_RUN_TEST_FIXTURE(parc_JSONArray); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_JSONArray) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_JSONArray) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(parc_JSONArray) +{ + LONGBOW_RUN_TEST_CASE(parc_JSONArray, parcJSONArray_CreateRelease); + LONGBOW_RUN_TEST_CASE(parc_JSONArray, parcJSONArray_Equals); + LONGBOW_RUN_TEST_CASE(parc_JSONArray, parcJSONArray_AddValue); + LONGBOW_RUN_TEST_CASE(parc_JSONArray, parcJSONArray_GetLength); + LONGBOW_RUN_TEST_CASE(parc_JSONArray, parcJSONArray_GetValue); + LONGBOW_RUN_TEST_CASE(parc_JSONArray, parcJSONArray_BuildString); + LONGBOW_RUN_TEST_CASE(parc_JSONArray, parcJSONArray_ToString); + LONGBOW_RUN_TEST_CASE(parc_JSONArray, parcJSONArray_Display); +} + +LONGBOW_TEST_FIXTURE_SETUP(parc_JSONArray) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(parc_JSONArray) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("Errors %s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(parc_JSONArray, parcJSONArray_CreateRelease) +{ + PARCJSONArray *expected = parcJSONArray_Create(); + parcJSONArray_AssertValid(expected); + assertNotNull(expected, "Expected non-null return value from parcJSONArray_Create"); + + PARCJSONArray *actual = parcJSONArray_Acquire(expected); + parcJSONArray_AssertValid(actual); + + parcJSONArray_Release(&actual); + assertNull(actual, "Expected null value set by parcJSONArray_Release"); + parcJSONArray_AssertValid(expected); + + parcJSONArray_Release(&expected); + assertNull(expected, "Expected null value set by parcJSONArray_Release"); +} + +LONGBOW_TEST_CASE(parc_JSONArray, parcJSONArray_Equals) +{ + PARCJSONArray *x = parcJSONArray_Create(); + PARCJSONArray *y = parcJSONArray_Create(); + PARCJSONArray *z = parcJSONArray_Create(); + + PARCJSONArray *notEqual1 = parcJSONArray_Create(); + PARCJSONValue *value = parcJSONValue_CreateFromCString("Hello"); + parcJSONArray_AddValue(notEqual1, value); + parcJSONValue_Release(&value); + + parcObjectTesting_AssertEqualsFunction(parcJSONArray_Equals, x, y, z, notEqual1); + + parcJSONArray_Release(&x); + parcJSONArray_Release(&y); + parcJSONArray_Release(&z); + parcJSONArray_Release(¬Equal1); +} + +LONGBOW_TEST_CASE(parc_JSONArray, parcJSONArray_AddValue) +{ + PARCJSONArray *expected = parcJSONArray_Create(); + PARCJSONValue *value = parcJSONValue_CreateFromInteger(10); + parcJSONArray_AddValue(expected, value); + parcJSONValue_Release(&value); + + parcJSONArray_Release(&expected); +} + +LONGBOW_TEST_CASE(parc_JSONArray, parcJSONArray_GetLength) +{ + PARCJSONArray *expected = parcJSONArray_Create(); + PARCJSONValue *value = parcJSONValue_CreateFromInteger(10); + parcJSONArray_AddValue(expected, value); + parcJSONValue_Release(&value); + assertTrue(parcJSONArray_GetLength(expected) == 1, "Expected a length of 1"); + + parcJSONArray_Release(&expected); +} + +LONGBOW_TEST_CASE(parc_JSONArray, parcJSONArray_GetValue) +{ + PARCJSONArray *array = parcJSONArray_Create(); + PARCJSONValue *expected = parcJSONValue_CreateFromInteger(10); + parcJSONArray_AddValue(array, expected); + + PARCJSONValue *actual = parcJSONArray_GetValue(array, 0); + + assertTrue(expected == actual, "Expected different value"); + + parcJSONValue_Release(&expected); + parcJSONArray_Release(&array); +} + +LONGBOW_TEST_CASE(parc_JSONArray, parcJSONArray_BuildString) +{ + PARCJSONArray *array = parcJSONArray_Create(); + PARCJSONValue *expected = parcJSONValue_CreateFromInteger(10); + parcJSONArray_AddValue(array, expected); + + PARCBufferComposer *composer = parcBufferComposer_Create(); + parcJSONArray_BuildString(array, composer, false); + + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + parcBufferComposer_Release(&composer); + char *result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + + assertTrue(strlen(result) > 0, "Expected non-empty string result"); + + parcMemory_Deallocate((void **) &result); + + composer = parcBufferComposer_Create(); + parcJSONArray_BuildString(array, composer, true); + tempBuffer = parcBufferComposer_ProduceBuffer(composer); + parcBufferComposer_Release(&composer); + result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + + assertTrue(strlen(result) > 0, "Expected non-empty string result"); + + parcMemory_Deallocate((void **) &result); + + parcJSONValue_Release(&expected); + parcJSONArray_Release(&array); +} + +LONGBOW_TEST_CASE(parc_JSONArray, parcJSONArray_ToString) +{ + PARCJSONArray *array = parcJSONArray_Create(); + PARCJSONValue *expected = parcJSONValue_CreateFromInteger(10); + parcJSONArray_AddValue(array, expected); + parcJSONValue_Release(&expected); + + const char *string = parcJSONArray_ToString(array); + + parcMemory_Deallocate((void **) &string); + + parcJSONArray_Release(&array); +} + +LONGBOW_TEST_CASE(parc_JSONArray, parcJSONArray_Display) +{ + PARCJSONArray *array = parcJSONArray_Create(); + PARCJSONValue *expected = parcJSONValue_CreateFromInteger(10); + parcJSONArray_AddValue(array, expected); + parcJSONValue_Release(&expected); + + parcJSONArray_Display(array, 0); + + parcJSONArray_Release(&array); +} + +LONGBOW_TEST_FIXTURE(Static) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Static) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Static) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_JSONArray); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_JSONPair.c b/libparc/parc/algol/test/test_parc_JSONPair.c new file mode 100644 index 00000000..b8c0c3f9 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_JSONPair.c @@ -0,0 +1,421 @@ +/* + * 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 "../parc_JSONPair.c" +#include <LongBow/unit-test.h> + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. + +#include <stdio.h> +#include <fcntl.h> +#include <inttypes.h> + +#include "../parc_List.h" +#include "../parc_ArrayList.h" +#include "../parc_SafeMemory.h" +#include "../parc_Memory.h" +#include <parc/testing/parc_ObjectTesting.h> + +LONGBOW_TEST_RUNNER(parc_JSONPair) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Static); + LONGBOW_RUN_TEST_FIXTURE(JSONPair); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_JSONPair) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_JSONPair) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(JSONPair) +{ + LONGBOW_RUN_TEST_CASE(JSONPair, parcJSONPair_CreateAcquireRelease); + LONGBOW_RUN_TEST_CASE(JSONPair, parcJSONPair_BuildString); + LONGBOW_RUN_TEST_CASE(JSONPair, parcJSONPair_ToString); + LONGBOW_RUN_TEST_CASE(JSONPair, parcJSONPair_Display); + LONGBOW_RUN_TEST_CASE(JSONPair, parcJSONPair_Equals); + LONGBOW_RUN_TEST_CASE(JSONPair, parcJSONPair_Parser); + + LONGBOW_RUN_TEST_CASE(JSONPair, parcJSONPair_CreateNULL); + LONGBOW_RUN_TEST_CASE(JSONPair, parcJSONPair_CreateValue); + LONGBOW_RUN_TEST_CASE(JSONPair, parcJSONPair_CreateString); + LONGBOW_RUN_TEST_CASE(JSONPair, parcJSONPair_CreateFromBoolean); + LONGBOW_RUN_TEST_CASE(JSONPair, parcJSONPair_CreateFromInteger); + LONGBOW_RUN_TEST_CASE(JSONPair, parcJSONPair_CreateFromFloat); + LONGBOW_RUN_TEST_CASE(JSONPair, parcJSONPair_CreateFromJSONArray); + LONGBOW_RUN_TEST_CASE(JSONPair, parcJSONPair_CreateFromJSON); +} + +LONGBOW_TEST_FIXTURE_SETUP(JSONPair) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(JSONPair) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("Errors %s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(JSONPair, parcJSONPair_CreateAcquireRelease) +{ + PARCBuffer *name = parcBuffer_WrapCString("name"); + + PARCBuffer *stringValue = parcBuffer_WrapCString("foo"); + PARCJSONValue *value = parcJSONValue_CreateFromString(stringValue); + parcBuffer_Release(&stringValue); + + PARCJSONPair *pair = parcJSONPair_Create(name, value); + + assertTrue(parcBuffer_Equals(name, pair->name), + "Expected '%s' actual '%s'", parcBuffer_ToString(name), parcBuffer_ToString(pair->name)); + assertTrue(value == pair->value, + "Expected %p' actual %p", (void *) value, (void *) pair->value); + + PARCJSONPair *reference = parcJSONPair_Acquire(pair); + + assertTrue(reference == pair, + "Expected parcJSONPair_Acquire to return the same pointer as the original."); + + parcBuffer_Release(&name); + parcJSONValue_Release(&value); + parcJSONPair_Release(&reference); + parcJSONPair_Release(&pair); +} + +LONGBOW_TEST_CASE(JSONPair, parcJSONPair_Display) +{ + PARCBuffer *name = parcBuffer_WrapCString("name"); + PARCBuffer *value = parcBuffer_WrapCString("foo"); + + PARCJSONValue *jsonValue = parcJSONValue_CreateFromString(value); + parcBuffer_Release(&value); + PARCJSONPair *pair = parcJSONPair_Create(name, jsonValue); + parcBuffer_Release(&name); + parcJSONValue_Release(&jsonValue); + + parcJSONPair_Display(pair, 0); + + parcJSONPair_Release(&pair); +} + +LONGBOW_TEST_CASE(JSONPair, parcJSONPair_BuildString) +{ + PARCBuffer *name = parcBuffer_WrapCString("name"); + PARCBuffer *value = parcBuffer_WrapCString("foo/bar"); + + PARCJSONValue *jsonValue = parcJSONValue_CreateFromString(value); + parcBuffer_Release(&value); + PARCJSONPair *pair = parcJSONPair_Create(name, jsonValue); + parcBuffer_Release(&name); + parcJSONValue_Release(&jsonValue); + + // umcompressed + PARCBufferComposer *composer = parcBufferComposer_Create(); + parcJSONPair_BuildString(pair, composer, false); + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + char *actual = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + parcBufferComposer_Release(&composer); + + char *expected = "\"name\" : \"foo\\/bar\""; + assertTrue(strcmp(expected, actual) == 0, + "Expected '%s' actual '%s'", expected, actual); + parcMemory_Deallocate((void **) &actual); + + // compressed + composer = parcBufferComposer_Create(); + parcJSONPair_BuildString(pair, composer, true); + tempBuffer = parcBufferComposer_ProduceBuffer(composer); + actual = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + parcBufferComposer_Release(&composer); + + expected = "\"name\":\"foo/bar\""; + assertTrue(strcmp(expected, actual) == 0, + "Expected '%s' actual '%s'", expected, actual); + parcMemory_Deallocate((void **) &actual); + + parcJSONPair_Release(&pair); +} + +LONGBOW_TEST_CASE(JSONPair, parcJSONPair_ToString) +{ + PARCBuffer *name = parcBuffer_WrapCString("name"); + PARCBuffer *value = parcBuffer_WrapCString("foo"); + + PARCJSONValue *jsonValue = parcJSONValue_CreateFromString(value); + parcBuffer_Release(&value); + PARCJSONPair *pair = parcJSONPair_Create(name, jsonValue); + parcBuffer_Release(&name); + parcJSONValue_Release(&jsonValue); + + char *expected = "\"name\" : \"foo\""; + char *actual = parcJSONPair_ToString(pair); + + assertTrue(strcmp(expected, actual) == 0, + "Expected '%s' actual '%s'", expected, actual); + parcMemory_Deallocate((void **) &actual); + + parcJSONPair_Release(&pair); +} + +LONGBOW_TEST_CASE(JSONPair, parcJSONPair_CreateNULL) +{ + char *name = "MyNull"; + PARCBuffer *expectedName = parcBuffer_AllocateCString(name); + + PARCJSONPair *pair = parcJSONPair_CreateFromNULL(name); + + assertTrue(parcBuffer_Equals(expectedName, parcJSONPair_GetName(pair)), + "Expected name '%s', got '%s'", name, parcBuffer_ToString(parcJSONPair_GetName(pair))); + + assertTrue(parcJSONValue_IsNull(parcJSONPair_GetValue(pair)), + "Expected a JSON Null value."); + + parcJSONPair_Release(&pair); + parcBuffer_Release(&expectedName); +} + +LONGBOW_TEST_CASE(JSONPair, parcJSONPair_CreateValue) +{ + char *name = "MyNull"; + PARCBuffer *expectedName = parcBuffer_AllocateCString(name); + PARCJSONValue *value = parcJSONValue_CreateFromCString("Some Pig"); + + PARCJSONPair *pair = parcJSONPair_CreateFromJSONValue(name, value); + + assertTrue(parcBuffer_Equals(expectedName, parcJSONPair_GetName(pair)), + "Expected name '%s', got '%s'", name, parcBuffer_ToString(parcJSONPair_GetName(pair))); + + assertTrue(parcJSONValue_IsString(parcJSONPair_GetValue(pair)), + "Expected a String value."); + + assertTrue(parcJSONPair_GetValue(pair) == value, "Expect values to be equal"); + + parcJSONValue_Release(&value); + parcJSONPair_Release(&pair); + parcBuffer_Release(&expectedName); +} + +LONGBOW_TEST_CASE(JSONPair, parcJSONPair_CreateString) +{ + char *name = "MyNull"; + char *value = "value"; + PARCBuffer *expectedName = parcBuffer_AllocateCString(name); + PARCJSONValue *expectedValue = parcJSONValue_CreateFromCString(value); + + PARCJSONPair *pair = parcJSONPair_CreateFromString(name, value); + + assertTrue(parcBuffer_Equals(expectedName, parcJSONPair_GetName(pair)), + "Expected name '%s', got '%s'", name, parcBuffer_ToString(parcJSONPair_GetName(pair))); + assertTrue(parcJSONValue_Equals(expectedValue, parcJSONPair_GetValue(pair)), + "Expected value '%s', Got '%s'", value, parcBuffer_ToString(parcJSONValue_GetString(parcJSONPair_GetValue(pair)))); + assertTrue(parcJSONValue_IsString(parcJSONPair_GetValue(pair)), + "Expected a JSON String value."); + + parcJSONPair_Release(&pair); + parcBuffer_Release(&expectedName); + parcJSONValue_Release(&expectedValue); +} + +LONGBOW_TEST_CASE(JSONPair, parcJSONPair_CreateFromBoolean) +{ + char *name = "MyNull"; + bool value = true; + PARCBuffer *expectedName = parcBuffer_AllocateCString(name); + PARCJSONValue *expectedValue = parcJSONValue_CreateFromBoolean(value); + + PARCJSONPair *pair = parcJSONPair_CreateFromBoolean(name, value); + + assertTrue(parcBuffer_Equals(expectedName, parcJSONPair_GetName(pair)), + "Expected name '%s', got '%s'", name, parcBuffer_ToString(parcJSONPair_GetName(pair))); + assertTrue(parcJSONValue_Equals(expectedValue, parcJSONPair_GetValue(pair)), + "Expected value '%d', Got '%d'", value, parcJSONValue_GetBoolean(parcJSONPair_GetValue(pair))); + + assertTrue(parcJSONValue_IsBoolean(parcJSONPair_GetValue(pair)), + "Expected a JSON Boolean value."); + + parcJSONPair_Release(&pair); + parcBuffer_Release(&expectedName); + parcJSONValue_Release(&expectedValue); +} + +LONGBOW_TEST_CASE(JSONPair, parcJSONPair_CreateFromInteger) +{ + char *name = "MyNull"; + int value = 31415; + PARCBuffer *expectedName = parcBuffer_AllocateCString(name); + PARCJSONValue *expectedValue = parcJSONValue_CreateFromInteger(value); + + PARCJSONPair *pair = parcJSONPair_CreateFromInteger(name, value); + + assertTrue(parcBuffer_Equals(expectedName, parcJSONPair_GetName(pair)), + "Expected name '%s', got '%s'", name, parcBuffer_ToString(parcJSONPair_GetName(pair))); + assertTrue(parcJSONValue_Equals(expectedValue, parcJSONPair_GetValue(pair)), + "Expected value '%d', Got '%" PRIi64 "'", value, parcJSONValue_GetInteger(parcJSONPair_GetValue(pair))); + + assertTrue(parcJSONValue_IsNumber(parcJSONPair_GetValue(pair)), + "Expected a JSON Integer value."); + + parcJSONPair_Release(&pair); + parcBuffer_Release(&expectedName); + parcJSONValue_Release(&expectedValue); +} + +LONGBOW_TEST_CASE(JSONPair, parcJSONPair_CreateFromFloat) +{ + char *name = "MyNull"; + double value = 3.1; + PARCBuffer *expectedName = parcBuffer_AllocateCString(name); + PARCJSONValue *expectedValue = parcJSONValue_CreateFromFloat(value); + + PARCJSONPair *pair = parcJSONPair_CreateFromDouble(name, value); + + assertTrue(parcBuffer_Equals(expectedName, parcJSONPair_GetName(pair)), + "Expected name '%s', got '%s'", name, parcBuffer_ToString(parcJSONPair_GetName(pair))); + assertTrue(parcJSONValue_Equals(expectedValue, parcJSONPair_GetValue(pair)), + "Expected %g, got %Lg", value, parcJSONValue_GetFloat(parcJSONPair_GetValue(pair))); + + assertTrue(parcJSONValue_IsNumber(parcJSONPair_GetValue(pair)), + "Expected a JSON number value."); + + parcJSONPair_Release(&pair); + parcBuffer_Release(&expectedName); + parcJSONValue_Release(&expectedValue); +} + +LONGBOW_TEST_CASE(JSONPair, parcJSONPair_CreateFromJSONArray) +{ + char *name = "MyNull"; + PARCJSONArray *array = parcJSONArray_Create(); + + PARCBuffer *expectedName = parcBuffer_AllocateCString(name); + PARCJSONValue *expectedValue = parcJSONValue_CreateFromJSONArray(array); + + PARCJSONPair *pair = parcJSONPair_CreateFromJSONArray(name, array); + parcJSONArray_Release(&array); + + assertTrue(parcBuffer_Equals(expectedName, parcJSONPair_GetName(pair)), + "Expected name '%s', got '%s'", name, parcBuffer_ToString(parcJSONPair_GetName(pair))); + assertTrue(parcJSONValue_Equals(expectedValue, parcJSONPair_GetValue(pair)), + "Expected the value to be equal the same array provided"); + + assertTrue(parcJSONValue_IsArray(parcJSONPair_GetValue(pair)), + "Expected a JSON Array value."); + + parcJSONPair_Release(&pair); + parcBuffer_Release(&expectedName); + parcJSONValue_Release(&expectedValue); +} + +LONGBOW_TEST_CASE(JSONPair, parcJSONPair_CreateFromJSON) +{ + char *name = "MyNull"; + PARCJSON *value = parcJSON_Create(); + + PARCBuffer *expectedName = parcBuffer_AllocateCString(name); + PARCJSONValue *expectedValue = parcJSONValue_CreateFromJSON(value); + + PARCJSONPair *pair = parcJSONPair_CreateFromJSON(name, value); + + assertTrue(parcBuffer_Equals(expectedName, parcJSONPair_GetName(pair)), + "Expected name '%s', got '%s'", name, parcBuffer_ToString(parcJSONPair_GetName(pair))); + assertTrue(parcJSONValue_Equals(expectedValue, parcJSONPair_GetValue(pair)), + "Expected %s", parcJSON_ToString(value)); + + assertTrue(parcJSONValue_IsJSON(parcJSONPair_GetValue(pair)), + "Expected a JSON Object value."); + + parcJSONPair_Release(&pair); + parcBuffer_Release(&expectedName); + parcJSONValue_Release(&expectedValue); + parcJSON_Release(&value); +} + +LONGBOW_TEST_CASE(JSONPair, parcJSONPair_Equals) +{ + char *name = "MyNull"; + char *unequalName = "foo"; + int value = 31415; + int unequalValue = 141; + + PARCJSONPair *pair = parcJSONPair_CreateFromInteger(name, value); + PARCJSONPair *y = parcJSONPair_CreateFromInteger(name, value); + PARCJSONPair *z = parcJSONPair_CreateFromInteger(name, value); + PARCJSONPair *unequal1 = parcJSONPair_CreateFromInteger(name, unequalValue); + PARCJSONPair *unequal2 = parcJSONPair_CreateFromInteger(unequalName, unequalValue); + + parcObjectTesting_AssertEqualsFunction(parcJSONPair_Equals, pair, y, z, unequal1, unequal2); + + parcJSONPair_Release(&pair); + parcJSONPair_Release(&y); + parcJSONPair_Release(&z); + parcJSONPair_Release(&unequal1); + parcJSONPair_Release(&unequal2); +} + +LONGBOW_TEST_CASE(JSONPair, parcJSONPair_Parser) +{ + PARCBuffer *buffer = parcBuffer_AllocateCString("\"name\" : \"value\""); + + PARCJSONParser *parser = parcJSONParser_Create(buffer); + PARCJSONPair *pair = parcJSONPair_Parser(parser); + + assertTrue(parcBuffer_Position(parcJSONPair_GetName(pair)) == 0, "Expected the JSONPair name buffer to be 'reset'"); + + parcJSONPair_Release(&pair); + parcJSONParser_Release(&parser); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_FIXTURE(Static) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Static) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Static) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_JSONPair); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_JSONParser.c b/libparc/parc/algol/test/test_parc_JSONParser.c new file mode 100755 index 00000000..63bcea8d --- /dev/null +++ b/libparc/parc/algol/test/test_parc_JSONParser.c @@ -0,0 +1,310 @@ +/* + * 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 <LongBow/unit-test.h> + +#include <stdio.h> +#include <fcntl.h> +#include <math.h> + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. + +#include "../parc_JSONParser.c" + +#include <parc/algol/parc_JSONValue.h> + +#include <parc/algol/parc_List.h> +#include <parc/algol/parc_ArrayList.h> +#include <parc/algol/parc_SafeMemory.h> +#include <parc/algol/parc_StdlibMemory.h> +#include <parc/algol/parc_Memory.h> + +LONGBOW_TEST_RUNNER(parc_JSONParser) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Static); + LONGBOW_RUN_TEST_FIXTURE(JSONParse_CreateAcquireRelease); + LONGBOW_RUN_TEST_FIXTURE(JSONParse); +// LONGBOW_RUN_TEST_FIXTURE(Performance); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_JSONParser) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_JSONParser) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + + +LONGBOW_TEST_FIXTURE(JSONParse_CreateAcquireRelease) +{ + LONGBOW_RUN_TEST_CASE(JSONParse_CreateAcquireRelease, parcJSONParser_Create); + LONGBOW_RUN_TEST_CASE(JSONParse_CreateAcquireRelease, parcJSONParser_AcquireRelease); +} + +LONGBOW_TEST_FIXTURE_SETUP(JSONParse_CreateAcquireRelease) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(JSONParse_CreateAcquireRelease) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("Errors %s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(JSONParse_CreateAcquireRelease, parcJSONParser_Create) +{ + char *string = "\"string\""; + + PARCBuffer *buffer = parcBuffer_WrapCString(string); + + PARCJSONParser *parser = parcJSONParser_Create(buffer); + + parcJSONParser_Release(&parser); + parcBuffer_Release(&buffer); + assertNull(parser, + "Expected parcJSONParser_Release to set the reference pointer to NULL"); +} + +LONGBOW_TEST_CASE(JSONParse_CreateAcquireRelease, parcJSONParser_AcquireRelease) +{ + char *string = "\"string\""; + + PARCBuffer *buffer = parcBuffer_WrapCString(string); + + PARCJSONParser *expected = parcJSONParser_Create(buffer); + + PARCJSONParser *actual = parcJSONParser_Acquire(expected); + assertTrue(actual == expected, + "Expected the acquired reference to be the same as the original instance."); + + parcJSONParser_Release(&actual); + assertNull(actual, + "Expected parcJSONParser_Release to set the reference pointer to NULL"); + + parcJSONParser_Release(&expected); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_FIXTURE(JSONParse) +{ + LONGBOW_RUN_TEST_CASE(JSONParse, parcJSONString_Parser); + LONGBOW_RUN_TEST_CASE(JSONParse, parcJSONParser_RequireString_Fail); + LONGBOW_RUN_TEST_CASE(JSONParse, parcJSONString_Parser_Quoted); + LONGBOW_RUN_TEST_CASE(JSONParse, parcJSON_Parse); + + LONGBOW_RUN_TEST_CASE(JSONParse, parcJSON_ParseFile); + LONGBOW_RUN_TEST_CASE(JSONParse, parcJSON_ParseFileToString); +} + +LONGBOW_TEST_FIXTURE_SETUP(JSONParse) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(JSONParse) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("Errors %s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(JSONParse, parcJSONString_Parser) +{ + char *string = "\"\\\" \\\\ \\b \\f \\n \\r \\t \\/\""; + PARCBuffer *buffer = parcBuffer_WrapCString(string); + + PARCJSONParser *parser = parcJSONParser_Create(buffer); + + PARCBuffer *expected = parcBuffer_AllocateCString("\" \\ \b \f \n \r \t /"); + PARCBuffer *actual = parcJSONParser_ParseString(parser); + + assertTrue(parcBuffer_Equals(expected, actual), "Expected string"); + + parcBuffer_Release(&actual); + parcBuffer_Release(&expected); + parcJSONParser_Release(&parser); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(JSONParse, parcJSONParser_RequireString_Fail) +{ + char *string = "\"string\""; + char *requiredString = "foo"; + + PARCBuffer *buffer = parcBuffer_WrapCString(string); + + PARCJSONParser *parser = parcJSONParser_Create(buffer); + + bool actual = parcJSONParser_RequireString(parser, requiredString); + + assertFalse(actual, "Expected parcJSONParser_RequireString to fail"); + + parcJSONParser_Release(&parser); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(JSONParse, parcJSONString_Parser_Quoted) +{ + char *string = "\"str\\\"ing\""; + + PARCBuffer *buffer = parcBuffer_WrapCString(string); + + PARCJSONParser *parser = parcJSONParser_Create(buffer); + + PARCBuffer *expected = parcBuffer_WrapCString("str\"ing"); + PARCBuffer *actual = parcJSONParser_ParseString(parser); + + assertTrue(parcBuffer_Equals(expected, actual), "Expected string"); + + parcBuffer_Release(&actual); + parcBuffer_Release(&expected); + parcJSONParser_Release(&parser); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(JSONParse, parcJSON_Parse) +{ + char *expected = "{ \"string\" : \"string\", \"null\" : null, \"true\" : true, \"false\" : false, \"integer\" : 31415, \"float\" : 3.141500, \"array\" : [ null, false, true, 31415, \"string\", [ null, false, true, 31415, \"string\" ], { } ] }"; + + expected = "{ \"integer\" : 31415 }"; + + expected = "{ \"string\" : \"string\", \"null\" : null, \"true\" : true, \"false\" : false, \"integer\" : 31415, \"array\" : [ null, false, true, 31415, \"string\", [ null, false, true, 31415, \"string\" ], { \"string\" : \"string\" } ] }"; + + PARCJSON *json = parcJSON_ParseString(expected); + assertNotNull(json, "Parse error for %s", expected); + + char *actual = parcJSON_ToString(json); + + assertTrue(strcmp(expected, actual) == 0, "Expected %s, actual %s", expected, actual); + + printf("%s\n", actual); + parcMemory_Deallocate((void **) &actual); + + parcJSON_Release(&json); +} + +LONGBOW_TEST_CASE(JSONParse, parcJSON_ParseFile) +{ + char *string = NULL; + size_t nread = longBowDebug_ReadFile("data.json", &string); + assertTrue(nread != -1, "Cannot read '%s'", "data.json"); + + PARCJSON *json = parcJSON_ParseString(string); + + assertNotNull(json, "parcJSON_ParseString failed"); + + // assertTrue(longBowDebug_WriteFile("/tmp/test_parc_JSON.json", actual, strlen(actual)) != 0, + // "Can't write file"); + + parcJSON_Release(&json); +} + +LONGBOW_TEST_CASE(JSONParse, parcJSON_ParseFileToString) +{ + char *string = NULL; + size_t nread = longBowDebug_ReadFile("data.json", &string); + assertTrue(nread != -1, "Cannot read '%s'", "data.json"); + + PARCJSON *json = parcJSON_ParseString(string); + + assertNotNull(json, "parcJSON_ParseString failed"); + + char *actual = parcJSON_ToString(json); + + // assertTrue(longBowDebug_WriteFile("/tmp/test_parc_JSON.json", actual, strlen(actual)) != 0, + // "Can't write file"); + + parcMemory_Deallocate((void **) &actual); + + parcJSON_Release(&json); +} + +LONGBOW_TEST_FIXTURE(Static) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Static) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Static) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Performance) +{ + LONGBOW_RUN_TEST_CASE(Performance, parcJSON_ParseFileToString); +} + +LONGBOW_TEST_FIXTURE_SETUP(Performance) +{ + parcMemory_SetInterface(&PARCStdlibMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Performance) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Performance, parcJSON_ParseFileToString) +{ + char *string = NULL; + size_t nread = longBowDebug_ReadFile("citylots.json", &string); + assertTrue(nread != -1, "Cannot read '%s'", "citylots.json"); + + PARCJSON *json = parcJSON_ParseString(string); + + assertNotNull(json, "parcJSON_ParseString failed"); + + char *actual = parcJSON_ToString(json); + + // assertTrue(longBowDebug_WriteFile("/tmp/test_parc_JSON.json", actual, strlen(actual)) != 0, + // "Can't write file"); + + parcMemory_Deallocate((void **) &actual); + + parcJSON_Release(&json); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_JSONParser); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_JSONValue.c b/libparc/parc/algol/test/test_parc_JSONValue.c new file mode 100644 index 00000000..804eb690 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_JSONValue.c @@ -0,0 +1,1365 @@ +/* + * 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 the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. + +#include "../parc_JSONValue.c" +#include <LongBow/unit-test.h> + +#include <stdio.h> +#include <fcntl.h> +#include <inttypes.h> + +#include "../parc_List.h" +#include "../parc_ArrayList.h" +#include "../parc_SafeMemory.h" +#include "../parc_Memory.h" + +#include <parc/testing/parc_ObjectTesting.h> + +LONGBOW_TEST_RUNNER(parc_JSONValue) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Static); + LONGBOW_RUN_TEST_FIXTURE(JSONValue_CreateAcquireRelease); + LONGBOW_RUN_TEST_FIXTURE(JSONValue); + LONGBOW_RUN_TEST_FIXTURE(JSONValueParsing); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_JSONValue) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_JSONValue) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(JSONValue_CreateAcquireRelease) +{ + LONGBOW_RUN_TEST_CASE(JSONValue_CreateAcquireRelease, _createValue); + LONGBOW_RUN_TEST_CASE(JSONValue_CreateAcquireRelease, parcJSONValue_AcquireRelease); +} + +LONGBOW_TEST_FIXTURE_SETUP(JSONValue_CreateAcquireRelease) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(JSONValue_CreateAcquireRelease) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("Errors %s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(JSONValue_CreateAcquireRelease, _createValue) +{ + PARCJSONValue *result = parcJSONValue_CreateFromNULL(); + + assertNotNull(result, "Expected non-null return value from _createValue"); + assertTrue(parcJSONValue_IsNull(result), + "Expected PARCJSONValueType_Null"); + + parcJSONValue_Release(&result); + assertNull(result, "Expected parcJSONValue_Release to NULL the instance pointer."); +} + + +LONGBOW_TEST_CASE(JSONValue_CreateAcquireRelease, parcJSONValue_AcquireRelease) +{ + PARCJSONValue *result = parcJSONValue_CreateFromNULL(); + + assertNotNull(result, "Expected non-null return value from _createValue"); + assertTrue(parcJSONValue_IsNull(result), + "Expected PARCJSONValueType_Null"); + + PARCJSONValue *actual = parcJSONValue_Acquire(result); + assertTrue(result == actual, "Expected parcJSONValue_Acquire return value to be same as the original."); + + parcJSONValue_Release(&actual); + parcJSONValue_Release(&result); + assertNull(result, "Expected parcJSONValue_Release to NULL the instance pointer."); +} + + +LONGBOW_TEST_FIXTURE(JSONValue) +{ + LONGBOW_RUN_TEST_CASE(JSONValue, parcJSONValue_Create_NULL); + LONGBOW_RUN_TEST_CASE(JSONValue, parcJSONValue_Create_Boolean); + LONGBOW_RUN_TEST_CASE(JSONValue, parcJSONValue_Create_Float); + LONGBOW_RUN_TEST_CASE(JSONValue, parcJSONValue_Create_Integer); + LONGBOW_RUN_TEST_CASE(JSONValue, parcJSONValue_Create_String); + LONGBOW_RUN_TEST_CASE(JSONValue, parcJSONValue_Create_JSON); + LONGBOW_RUN_TEST_CASE(JSONValue, parcJSONValue_Create_Array); + LONGBOW_RUN_TEST_CASE(JSONValue, parcJSONValue_Create_Timespec); + LONGBOW_RUN_TEST_CASE(JSONValue, parcJSONValue_Create_Timeval); + + LONGBOW_RUN_TEST_CASE(JSONValue, parcJSONValue_IsValid); + + LONGBOW_RUN_TEST_CASE(JSONValue, parcJSONValue_Display); + LONGBOW_RUN_TEST_CASE(JSONValue, parcJSONValue_BuildString); + LONGBOW_RUN_TEST_CASE(JSONValue, parcJSONValue_ToString_NULL); + LONGBOW_RUN_TEST_CASE(JSONValue, parcJSONValue_ToString_Array); + LONGBOW_RUN_TEST_CASE(JSONValue, parcJSONValue_ToString_Boolean); + LONGBOW_RUN_TEST_CASE(JSONValue, parcJSONValue_ToString_Float); + LONGBOW_RUN_TEST_CASE(JSONValue, parcJSONValue_ToString_Integer); + LONGBOW_RUN_TEST_CASE(JSONValue, parcJSONValue_ToString_String); + LONGBOW_RUN_TEST_CASE(JSONValue, parcJSONValue_CreateCString); + LONGBOW_RUN_TEST_CASE(JSONValue, parcJSONValue_ToString_JSON); + + LONGBOW_RUN_TEST_CASE(JSONValue, parcJSONValue_Equals_NULL); + LONGBOW_RUN_TEST_CASE(JSONValue, parcJSONValue_Equals_Boolean); + LONGBOW_RUN_TEST_CASE(JSONValue, parcJSONValue_Equals_Integer); + LONGBOW_RUN_TEST_CASE(JSONValue, parcJSONValue_Equals_Float); + LONGBOW_RUN_TEST_CASE(JSONValue, parcJSONValue_Equals_String); + LONGBOW_RUN_TEST_CASE(JSONValue, parcJSONValue_Equals_Object); + LONGBOW_RUN_TEST_CASE(JSONValue, parcJSONValue_Equals_Array); +} + +LONGBOW_TEST_FIXTURE_SETUP(JSONValue) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(JSONValue) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("Errors %s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(JSONValue, parcJSONValue_IsValid) +{ + bool actual = parcJSONValue_IsValid(NULL); + assertFalse(actual, "Expected a NULL value to be invalid"); + + PARCJSONValue *value = parcJSONValue_CreateFromNULL(); + + actual = parcJSONValue_IsValid(value); + parcJSONValue_Release(&value); + assertTrue(actual, "Expected a NULL value to be invalid"); +} + +LONGBOW_TEST_CASE(JSONValue, parcJSONValue_Create_JSON) +{ + PARCJSON *json = parcJSON_Create(); + PARCJSONValue *value = parcJSONValue_CreateFromJSON(json); + + assertTrue(parcJSONValue_IsJSON(value), + "Expected PARCJSONValueType_JSON"); + + assertTrue(parcJSONValue_GetJSON(value) == json, + "Expected parcJSONValue_GetJSON to return the original instance pointer."); + parcJSONValue_Release(&value); + assertNull(value, "Expected NULL pointer."); + parcJSON_Release(&json); +} + +LONGBOW_TEST_CASE(JSONValue, parcJSONValue_Create_Timeval) +{ + struct timeval timeval = { .tv_sec = 42, .tv_usec = 23 }; + PARCJSONValue *value = parcJSONValue_CreateFromTimeval(&timeval); + + assertTrue(parcJSONValue_IsJSON(value), + "Expected PARCJSONValueType_JSON"); + + struct timeval actual; + parcJSONValue_GetTimeval(value, &actual); + assertTrue(timeval.tv_sec == actual.tv_sec, "Expected seconds to be equal."); + assertTrue(timeval.tv_usec == actual.tv_usec, "Expected seconds to be equal."); + + parcJSONValue_Release(&value); +} + +LONGBOW_TEST_CASE(JSONValue, parcJSONValue_Create_Timespec) +{ + struct timespec timespec = { .tv_sec = 42, .tv_nsec = 23 }; + PARCJSONValue *value = parcJSONValue_CreateFromTimespec(×pec); + + assertTrue(parcJSONValue_IsJSON(value), + "Expected PARCJSONValueType_JSON"); + + struct timespec testTS; + parcJSONValue_GetTimespec(value, &testTS); + assertTrue(memcmp(×pec, &testTS, sizeof(struct timespec)) == 0, + "Expected parcJSONValue_GetTimespec to return the original instance pointer."); + parcJSONValue_Release(&value); +} + +LONGBOW_TEST_CASE(JSONValue, parcJSONValue_Create_NULL) +{ + PARCJSONValue *value = parcJSONValue_CreateFromNULL(); + + assertTrue(value->type == PARCJSONValueType_Null, + "Expected PARCJSONValueType_Null, actual %d", value->type); + assertTrue(parcJSONValue_IsNull(value), + "Expected PARCJSONValueType_Null"); + parcJSONValue_Release(&value); + assertNull(value, "Expected NULL pointer."); +} + +LONGBOW_TEST_CASE(JSONValue, parcJSONValue_Create_Boolean) +{ + bool expected = true; + PARCJSONValue *value = parcJSONValue_CreateFromBoolean(expected); + + assertTrue(value->type == PARCJSONValueType_Boolean, + "Expected PARCJSONValueType_BooleanON_VALUE_BOOLEAN, actual %d", value->type); + assertTrue(value->value.boolean == expected, "Expected %d actual %d", expected, value->value.boolean); + + assertTrue(parcJSONValue_IsBoolean(value), + "Expected PARCJSONValueType_Boolean"); + assertTrue(parcJSONValue_GetBoolean(value), "Expected value to be true"); + parcJSONValue_Release(&value); + assertNull(value, "Expected NULL pointer."); +} + +LONGBOW_TEST_CASE(JSONValue, parcJSONValue_Create_Float) +{ + double expected = 3.1415; + PARCJSONValue *value = parcJSONValue_CreateFromFloat(expected); + + assertTrue(parcJSONValue_IsNumber(value), + "Expected parcJSONValue_IsNumber to be true."); + assertTrue(parcJSONValue_GetFloat(value) == expected, + "Expected %g, actual %Lg", expected, parcJSONValue_GetFloat(value)); + + char *expectedString = "3.141500"; + char *actualString = parcJSONValue_ToString(value); + assertTrue(strcmp(expectedString, actualString) == 0, "Exepcted %s, actual %s", expectedString, actualString); + parcMemory_Deallocate((void **) &actualString); + + parcJSONValue_Release(&value); + assertNull(value, "Expected NULL pointer."); +} + +LONGBOW_TEST_CASE(JSONValue, parcJSONValue_Create_Integer) +{ + int expected = 31415; + PARCJSONValue *value = parcJSONValue_CreateFromInteger(expected); + + assertTrue(parcJSONValue_IsNumber(value), + "Expected parcJSONValue_IsNumber"); + int64_t actual = parcJSONValue_GetInteger(value); + assertTrue(expected == actual, "Expected %d, actual %" PRIi64 "", expected, actual); + parcJSONValue_Release(&value); + assertNull(value, "Expected NULL pointer."); +} + +LONGBOW_TEST_CASE(JSONValue, parcJSONValue_Create_String) +{ + PARCBuffer *expected = parcBuffer_WrapCString("31415"); + PARCJSONValue *value = parcJSONValue_CreateFromString(expected); + + assertTrue(value->type == PARCJSONValueType_String, + "Expected parcJSONValueType.String, actual %d", value->type); + assertTrue(parcBuffer_Equals(value->value.string, expected), + "Expected %s actual %s", parcBuffer_ToString(expected), parcBuffer_ToString(value->value.string)); + assertTrue(parcJSONValue_IsString(value), + "Expected PARCJSONValueType_String"); + + assertTrue(parcBuffer_Equals(parcJSONValue_GetString(value), expected), "Expected value did not match actual value"); + parcJSONValue_Release(&value); + assertNull(value, "Expected NULL pointer."); + parcBuffer_Release(&expected); +} + +LONGBOW_TEST_CASE(JSONValue, parcJSONValue_CreateCString) +{ + PARCBuffer *expected = parcBuffer_WrapCString("31415"); + + PARCJSONValue *value = parcJSONValue_CreateFromCString("31415"); + + assertTrue(value->type == PARCJSONValueType_String, + "Expected parcJSONValueType.String, actual %d", value->type); + + assertTrue(parcBuffer_Equals(parcJSONValue_GetString(value), expected), "Assert:") + { + char *expectedString = parcBuffer_ToString(expected); + char *actualString = parcBuffer_ToString(parcJSONValue_GetString(value)); + printf("Expected '%s', actual '%s'", expectedString, actualString); + parcMemory_Deallocate((void **) &expectedString); + parcMemory_Deallocate((void **) &actualString); + } + + parcJSONValue_Release(&value); + assertNull(value, "Expected NULL pointer."); + parcBuffer_Release(&expected); +} + +LONGBOW_TEST_CASE(JSONValue, parcJSONValue_Create_Array) +{ + PARCJSONArray *array = parcJSONArray_Create(); + + PARCJSONValue *value = parcJSONValue_CreateFromNULL(); + parcJSONArray_AddValue(array, value); + parcJSONValue_Release(&value); + + value = parcJSONValue_CreateFromJSONArray(array); + + parcJSONValue_Release(&value); + + parcJSONArray_Release(&array); + assertNull(value, "Expected NULL pointer."); +} + +LONGBOW_TEST_CASE(JSONValue, parcJSONValue_BuildString) +{ + PARCJSONArray *array = parcJSONArray_Create(); + + PARCJSONValue *value = parcJSONValue_CreateFromNULL(); + parcJSONArray_AddValue(array, value); + parcJSONValue_Release(&value); + + value = parcJSONValue_CreateFromBoolean(false); + parcJSONArray_AddValue(array, value); + parcJSONValue_Release(&value); + + value = parcJSONValue_CreateFromBoolean(true); + parcJSONArray_AddValue(array, value); + parcJSONValue_Release(&value); + + value = parcJSONValue_CreateFromInteger(31415); + parcJSONArray_AddValue(array, value); + parcJSONValue_Release(&value); + + PARCBuffer *stringValue = parcBuffer_WrapCString("stringA/stringB"); + value = parcJSONValue_CreateFromString(stringValue); + parcJSONArray_AddValue(array, value); + parcJSONValue_Release(&value); + parcBuffer_Release(&stringValue); + + value = parcJSONValue_CreateFromJSONArray(array); + parcJSONArray_Release(&array); + + // Uncompacted + PARCBufferComposer *composer = parcBufferComposer_Create(); + parcJSONValue_BuildString(value, composer, false); + + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + char *actual = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + parcBufferComposer_Release(&composer); + + char *expected = "[ null, false, true, 31415, \"stringA\\/stringB\" ]"; + + assertTrue(strcmp(actual, expected) == 0, + "Expected '%s', actual '%s'", expected, actual); + + parcMemory_Deallocate((void **) &actual); + + // Compacted + composer = parcBufferComposer_Create(); + parcJSONValue_BuildString(value, composer, true); + + tempBuffer = parcBufferComposer_ProduceBuffer(composer); + actual = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + parcBufferComposer_Release(&composer); + + expected = "[null,false,true,31415,\"stringA/stringB\"]"; + + assertTrue(strcmp(actual, expected) == 0, + "Expected '%s', actual '%s'", expected, actual); + + parcMemory_Deallocate((void **) &actual); + + + parcJSONValue_Release(&value); +} + +LONGBOW_TEST_CASE(JSONValue, parcJSONValue_ToString_Array) +{ + PARCJSONArray *array = parcJSONArray_Create(); + + PARCJSONValue *value = parcJSONValue_CreateFromNULL(); + parcJSONArray_AddValue(array, value); + parcJSONValue_Release(&value); + + value = parcJSONValue_CreateFromBoolean(false); + parcJSONArray_AddValue(array, value); + parcJSONValue_Release(&value); + + value = parcJSONValue_CreateFromBoolean(true); + parcJSONArray_AddValue(array, value); + parcJSONValue_Release(&value); + + value = parcJSONValue_CreateFromInteger(31415); + parcJSONArray_AddValue(array, value); + parcJSONValue_Release(&value); + + PARCBuffer *stringValue = parcBuffer_WrapCString("stringA/stringB"); + value = parcJSONValue_CreateFromString(stringValue); + parcJSONArray_AddValue(array, value); + parcJSONValue_Release(&value); + parcBuffer_Release(&stringValue); + + value = parcJSONValue_CreateFromJSONArray(array); + parcJSONArray_Release(&array); + + char *expected = "[ null, false, true, 31415, \"stringA\\/stringB\" ]"; + char *actual = parcJSONValue_ToString(value); + + assertTrue(strcmp(actual, expected) == 0, + "Expected '%s', actual '%s'", expected, actual); + + parcMemory_Deallocate((void **) &actual); + parcJSONValue_Release(&value); +} + +LONGBOW_TEST_CASE(JSONValue, parcJSONValue_Display) +{ + PARCJSONValue *value = parcJSONValue_CreateFromNULL(); + parcJSONValue_Display(value, 0); + parcJSONValue_Release(&value); + + value = parcJSONValue_CreateFromBoolean(true); + parcJSONValue_Display(value, 0); + parcJSONValue_Release(&value); + + value = parcJSONValue_CreateFromCString("hello"); + parcJSONValue_Display(value, 0); + parcJSONValue_Release(&value); + + value = parcJSONValue_CreateFromFloat(3.14); + parcJSONValue_Display(value, 0); + parcJSONValue_Release(&value); + + value = parcJSONValue_CreateFromInteger(314); + parcJSONValue_Display(value, 0); + parcJSONValue_Release(&value); + + PARCJSONArray *array = parcJSONArray_Create(); + value = parcJSONValue_CreateFromJSONArray(array); + parcJSONValue_Display(value, 0); + parcJSONValue_Release(&value); + parcJSONArray_Release(&array); + + PARCJSON *json = parcJSON_Create(); + value = parcJSONValue_CreateFromJSON(json); + parcJSONValue_Display(value, 0); + parcJSONValue_Release(&value); + parcJSON_Release(&json); +} + +LONGBOW_TEST_CASE(JSONValue, parcJSONValue_ToString_NULL) +{ + char *expected = "null"; + PARCJSONValue *value = parcJSONValue_CreateFromNULL(); + + char *actual = parcJSONValue_ToString(value); + assertTrue(strcmp(actual, expected) == 0, "Expected '%s', actual '%s'", expected, actual); + + parcMemory_Deallocate((void **) &actual); + parcJSONValue_Release(&value); +} + +LONGBOW_TEST_CASE(JSONValue, parcJSONValue_ToString_Boolean) +{ + char *expected = "true"; + PARCJSONValue *value = parcJSONValue_CreateFromBoolean(expected); + + char *actual = parcJSONValue_ToString(value); + assertTrue(strcmp(actual, expected) == 0, "Expected '%s', actual '%s'", expected, actual); + + parcMemory_Deallocate((void **) &actual); + parcJSONValue_Release(&value); +} + +LONGBOW_TEST_CASE(JSONValue, parcJSONValue_ToString_Float) +{ + struct test_values { + char *string; + long double value; + long double error; + } successful[] = { + { "-0.0415e-12", -0.0415e-12, 0.00001e-12 }, + { "-0.0415e12", -0.0415e12, 0.00001e12 }, + { "-0.0415", -0.0415, 0.00001 }, + { "-3.0415", -3.0415, 0.00001 }, + { "123.456", 123.456, 0.0001 }, + { "123.456e78", 123.456e78, 0.0001e78 }, + { "123.456e-78", 123.456e-78, 0.0001e-78 }, + { "123.456e-78", 123.456e-78, 0.0001e-78 }, + { "4e1", 40.0, 0.0001e-78 }, + { NULL }, + }; + + for (int i = 0; successful[i].string != NULL; i++) { + PARCBuffer *buffer = parcBuffer_WrapCString(successful[i].string); + PARCJSONParser *parser = parcJSONParser_Create(buffer); + parcBuffer_Release(&buffer); + + PARCJSONValue *expected = _parcJSONValue_NumberParser(parser); + + char *string = parcJSONValue_ToString(expected); + assertTrue(strcmp(successful[i].string, string) == 0, + "Expected %s, actual %s", successful[i].string, string); + parcMemory_Deallocate((void **) &string); + + parcJSONValue_Release(&expected); + parcJSONParser_Release(&parser); + } +} + +LONGBOW_TEST_CASE(JSONValue, parcJSONValue_ToString_Integer) +{ + char *expected = "31415"; + PARCJSONValue *value = parcJSONValue_CreateFromInteger(31415); + + char *actual = parcJSONValue_ToString(value); + assertTrue(strcmp(actual, expected) == 0, + "Expected '%s', actual '%s'", expected, actual); + + parcMemory_Deallocate((void **) &actual); + parcJSONValue_Release(&value); +} + +LONGBOW_TEST_CASE(JSONValue, parcJSONValue_ToString_String) +{ + char *input = "31415\b"; + char *expected = "\"31415\\b\""; + + PARCBuffer *stringValue = parcBuffer_WrapCString(input); + PARCJSONValue *value = parcJSONValue_CreateFromString(stringValue); + parcBuffer_Release(&stringValue); + + char *actual = parcJSONValue_ToString(value); + assertTrue(strcmp(actual, expected) == 0, "Expected '%s', actual '%s'", expected, actual); + + parcMemory_Deallocate((void **) &actual); + parcJSONValue_Release(&value); +} + +LONGBOW_TEST_CASE(JSONValue, parcJSONValue_ToString_JSON) +{ + char *expected = "{ }"; + PARCJSON *json = parcJSON_Create(); + PARCJSONValue *value = parcJSONValue_CreateFromJSON(json); + parcJSON_Release(&json); + + char *actual = parcJSONValue_ToString(value); + assertTrue(strcmp(actual, expected) == 0, "Expected '%s', actual '%s'", expected, actual); + + parcMemory_Deallocate((void **) &actual); + parcJSONValue_Release(&value); +} + +LONGBOW_TEST_CASE(JSONValue, parcJSONValue_Equals_NULL) +{ + PARCJSONValue *example = parcJSONValue_CreateFromNULL(); + + PARCJSONValue *equal1 = parcJSONValue_CreateFromNULL(); + PARCJSONValue *equal2 = parcJSONValue_CreateFromNULL(); + + PARCBuffer *stringBuffer = parcBuffer_AllocateCString("Hello"); + PARCJSONValue *string = parcJSONValue_CreateFromString(stringBuffer); + parcBuffer_Release(&stringBuffer); + + parcObjectTesting_AssertEqualsFunction(parcJSONValue_Equals, example, equal1, equal2, string); + + parcJSONValue_Release(&string); + parcJSONValue_Release(&equal2); + parcJSONValue_Release(&equal1); + parcJSONValue_Release(&example); +} + +LONGBOW_TEST_CASE(JSONValue, parcJSONValue_Equals_Boolean) +{ + PARCJSONValue *example = parcJSONValue_CreateFromBoolean(true); + + PARCJSONValue *equal1 = parcJSONValue_CreateFromBoolean(true); + PARCJSONValue *equal2 = parcJSONValue_CreateFromBoolean(true); + + PARCJSONValue *unequal1 = parcJSONValue_CreateFromBoolean(false); + + PARCBuffer *stringBuffer = parcBuffer_AllocateCString("Hello"); + PARCJSONValue *string = parcJSONValue_CreateFromString(stringBuffer); + parcBuffer_Release(&stringBuffer); + + parcObjectTesting_AssertEqualsFunction(parcJSONValue_Equals, example, equal1, equal2, unequal1, string); + + parcJSONValue_Release(&string); + parcJSONValue_Release(&unequal1); + parcJSONValue_Release(&equal2); + parcJSONValue_Release(&equal1); + parcJSONValue_Release(&example); +} + +LONGBOW_TEST_CASE(JSONValue, parcJSONValue_Equals_Integer) +{ + PARCJSONValue *example = parcJSONValue_CreateFromInteger(31415); + + PARCJSONValue *equal1 = parcJSONValue_CreateFromInteger(31415); + PARCJSONValue *equal2 = parcJSONValue_CreateFromInteger(31415); + + PARCJSONValue *unequal1 = parcJSONValue_CreateFromInteger(4); + + PARCBuffer *stringBuffer = parcBuffer_AllocateCString("Hello"); + PARCJSONValue *string = parcJSONValue_CreateFromString(stringBuffer); + parcBuffer_Release(&stringBuffer); + + parcObjectTesting_AssertEqualsFunction(parcJSONValue_Equals, example, equal1, equal2, unequal1, string); + + parcJSONValue_Release(&string); + parcJSONValue_Release(&unequal1); + parcJSONValue_Release(&equal2); + parcJSONValue_Release(&equal1); + parcJSONValue_Release(&example); +} + +LONGBOW_TEST_CASE(JSONValue, parcJSONValue_Equals_Float) +{ + PARCJSONValue *example = parcJSONValue_CreateFromFloat(3.1415); + + PARCJSONValue *equal1 = parcJSONValue_CreateFromFloat(3.1415); + PARCJSONValue *equal2 = parcJSONValue_CreateFromFloat(3.1415); + + PARCJSONValue *unequal1 = parcJSONValue_CreateFromFloat(4.0); + + PARCBuffer *stringBuffer = parcBuffer_AllocateCString("Hello"); + PARCJSONValue *string = parcJSONValue_CreateFromString(stringBuffer); + parcBuffer_Release(&stringBuffer); + + parcObjectTesting_AssertEqualsFunction(parcJSONValue_Equals, example, equal1, equal2, unequal1, string); + + parcJSONValue_Release(&string); + parcJSONValue_Release(&unequal1); + parcJSONValue_Release(&equal2); + parcJSONValue_Release(&equal1); + parcJSONValue_Release(&example); +} + +LONGBOW_TEST_CASE(JSONValue, parcJSONValue_Equals_String) +{ + PARCBuffer *stringBuffer = parcBuffer_AllocateCString("Hello"); + PARCJSONValue *example = parcJSONValue_CreateFromString(stringBuffer); + parcBuffer_Release(&stringBuffer); + + stringBuffer = parcBuffer_AllocateCString("Hello"); + PARCJSONValue *equal1 = parcJSONValue_CreateFromString(stringBuffer); + parcBuffer_Release(&stringBuffer); + + stringBuffer = parcBuffer_AllocateCString("Hello"); + PARCJSONValue *equal2 = parcJSONValue_CreateFromString(stringBuffer); + parcBuffer_Release(&stringBuffer); + + PARCJSONValue *unequal1 = parcJSONValue_CreateFromFloat(4.0); + + stringBuffer = parcBuffer_AllocateCString("World"); + PARCJSONValue *string = parcJSONValue_CreateFromString(stringBuffer); + parcBuffer_Release(&stringBuffer); + + parcObjectTesting_AssertEqualsFunction(parcJSONValue_Equals, example, equal1, equal2, unequal1, string); + + parcJSONValue_Release(&string); + parcJSONValue_Release(&unequal1); + parcJSONValue_Release(&equal2); + parcJSONValue_Release(&equal1); + parcJSONValue_Release(&example); +} + +LONGBOW_TEST_CASE(JSONValue, parcJSONValue_Equals_Object) +{ + char *string = "{ \"name\" : 1, \"name2\" : 2 }"; + + PARCBuffer *buffer = parcBuffer_WrapCString(string); + PARCJSONParser *parser = parcJSONParser_Create(buffer); + parcBuffer_Release(&buffer); + + PARCJSONValue *x = parcJSONValue_ObjectParser(parser); + parcJSONParser_Release(&parser); + + assertTrue(parcJSONValue_IsJSON(x), "Expected a JSON Object value."); + + buffer = parcBuffer_WrapCString(string); + parser = parcJSONParser_Create(buffer); + parcBuffer_Release(&buffer); + + PARCJSONValue *y = parcJSONValue_ObjectParser(parser); + parcJSONParser_Release(&parser); + + buffer = parcBuffer_WrapCString(string); + parser = parcJSONParser_Create(buffer); + parcBuffer_Release(&buffer); + + PARCJSONValue *z = parcJSONValue_ObjectParser(parser); + parcJSONParser_Release(&parser); + + PARCJSONValue *unequal1 = parcJSONValue_CreateFromFloat(4.0); + + PARCJSON *json = parcJSON_Create(); + PARCJSONValue *unequal2 = parcJSONValue_CreateFromJSON(json); + parcJSON_Release(&json); + + parcObjectTesting_AssertEqualsFunction(parcJSONValue_Equals, x, y, z, unequal1, unequal2); + + parcJSONValue_Release(&x); + parcJSONValue_Release(&y); + parcJSONValue_Release(&z); + parcJSONValue_Release(&unequal1); + parcJSONValue_Release(&unequal2); +} + +LONGBOW_TEST_CASE(JSONValue, parcJSONValue_Equals_Array) +{ + char *string = "[ \"name\", 1, true, false, null, [ ], { } ]"; + + PARCBuffer *buffer = parcBuffer_WrapCString(string); + PARCJSONParser *parser = parcJSONParser_Create(buffer); + parcBuffer_Release(&buffer); + + PARCJSONValue *x = _parcJSONValue_ArrayParser(parser); + parcJSONParser_Release(&parser); + + assertTrue(parcJSONValue_IsArray(x), "Expected a JSON Array value."); + + buffer = parcBuffer_WrapCString(string); + parser = parcJSONParser_Create(buffer); + parcBuffer_Release(&buffer); + + PARCJSONValue *y = _parcJSONValue_ArrayParser(parser); + parcJSONParser_Release(&parser); + + buffer = parcBuffer_WrapCString(string); + parser = parcJSONParser_Create(buffer); + parcBuffer_Release(&buffer); + + PARCJSONValue *z = _parcJSONValue_ArrayParser(parser); + parcJSONParser_Release(&parser); + + PARCJSONValue *unequal1 = parcJSONValue_CreateFromFloat(4.0); + + PARCJSONArray *array = parcJSONArray_Create(); + PARCJSONValue *unequal2 = parcJSONValue_CreateFromJSONArray(array); + parcJSONArray_Release(&array); + + parcObjectTesting_AssertEqualsFunction(parcJSONValue_Equals, x, y, z, unequal1, unequal2); + + parcJSONValue_Release(&x); + parcJSONValue_Release(&y); + parcJSONValue_Release(&z); + parcJSONValue_Release(&unequal1); + parcJSONValue_Release(&unequal2); +} + +LONGBOW_TEST_FIXTURE(JSONValueParsing) +{ + LONGBOW_RUN_TEST_CASE(JSONValueParsing, _parcJSONValue_NullParser); + LONGBOW_RUN_TEST_CASE(JSONValueParsing, _parcJSONValue_NullParser_Bad); + LONGBOW_RUN_TEST_CASE(JSONValueParsing, _parcJSONValue_TrueParser); + LONGBOW_RUN_TEST_CASE(JSONValueParsing, _parcJSONValue_TrueParser_Bad); + LONGBOW_RUN_TEST_CASE(JSONValueParsing, _parcJSONValue_FalseParser); + LONGBOW_RUN_TEST_CASE(JSONValueParsing, _parcJSONValue_FalseParser_Bad); + LONGBOW_RUN_TEST_CASE(JSONValueParsing, _parcJSONValue_StringParser); + LONGBOW_RUN_TEST_CASE(JSONValueParsing, parcJSONValue_ObjectParser); + LONGBOW_RUN_TEST_CASE(JSONValueParsing, parcJSONValue_ObjectParser_Bad_Pair); + LONGBOW_RUN_TEST_CASE(JSONValueParsing, parcJSONValue_ObjectParser_Bad_Pair2); + LONGBOW_RUN_TEST_CASE(JSONValueParsing, parcJSONValue_ArrayParser); + LONGBOW_RUN_TEST_CASE(JSONValueParsing, _parcJSONValue_StringParser_BAD); + + LONGBOW_RUN_TEST_CASE(JSONValueParsing, parcJSONValue_NumberParser_BatchedFloat); + + LONGBOW_RUN_TEST_CASE(JSONValueParsing, parcJSONValue_Parser_Comma); + LONGBOW_RUN_TEST_CASE(JSONValueParsing, parcJSONValue_Parser_CloseBracket); + LONGBOW_RUN_TEST_CASE(JSONValueParsing, parcJSONValue_Parser_Null); + LONGBOW_RUN_TEST_CASE(JSONValueParsing, parcJSONValue_Parser_True); + LONGBOW_RUN_TEST_CASE(JSONValueParsing, parcJSONValue_Parser_False); + LONGBOW_RUN_TEST_CASE(JSONValueParsing, parcJSONValue_Parser_String); + LONGBOW_RUN_TEST_CASE(JSONValueParsing, parcJSONValue_Parser_Array); + LONGBOW_RUN_TEST_CASE(JSONValueParsing, parcJSONValue_Parser_Object); +} + +LONGBOW_TEST_FIXTURE_SETUP(JSONValueParsing) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(JSONValueParsing) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("Errors %s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(JSONValueParsing, _parcJSONValue_NullParser) +{ + char *string = "null"; + PARCBuffer *buffer = parcBuffer_WrapCString(string); + + PARCJSONParser *parser = parcJSONParser_Create(buffer); + PARCJSONValue *actual = _parcJSONValue_NullParser(parser); + + assertTrue(parcJSONValue_IsNull(actual), "Expected a JSON Null value."); + + parcJSONValue_Release(&actual); + parcJSONParser_Release(&parser); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(JSONValueParsing, _parcJSONValue_NullParser_Bad) +{ + char *string = "nulx"; + PARCBuffer *buffer = parcBuffer_WrapCString(string); + + PARCJSONParser *parser = parcJSONParser_Create(buffer); + PARCJSONValue *actual = _parcJSONValue_NullParser(parser); + + assertNull(actual, "Expected a NULL return value"); + + parcJSONParser_Release(&parser); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(JSONValueParsing, _parcJSONValue_TrueParser) +{ + char *string = "true"; + PARCBuffer *buffer = parcBuffer_WrapCString(string); + + PARCJSONParser *parser = parcJSONParser_Create(buffer); + PARCJSONValue *actual = _parcJSONValue_TrueParser(parser); + + assertTrue(parcJSONValue_IsBoolean(actual), "Expected a JSON Boolean value."); + assertTrue(parcJSONValue_GetBoolean(actual), "Expected true."); + + parcJSONValue_Release(&actual); + parcJSONParser_Release(&parser); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(JSONValueParsing, _parcJSONValue_TrueParser_Bad) +{ + char *string = "trux"; + PARCBuffer *buffer = parcBuffer_WrapCString(string); + + PARCJSONParser *parser = parcJSONParser_Create(buffer); + PARCJSONValue *actual = _parcJSONValue_TrueParser(parser); + + assertNull(actual, "Expected a NULL return value"); + + parcJSONParser_Release(&parser); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(JSONValueParsing, _parcJSONValue_FalseParser) +{ + char *string = "false"; + PARCBuffer *buffer = parcBuffer_WrapCString(string); + + PARCJSONParser *parser = parcJSONParser_Create(buffer); + PARCJSONValue *actual = _parcJSONValue_FalseParser(parser); + + assertTrue(parcJSONValue_IsBoolean(actual), "Expected a JSON Boolean value."); + assertFalse(parcJSONValue_GetBoolean(actual), "Expected false."); + + parcJSONValue_Release(&actual); + parcJSONParser_Release(&parser); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(JSONValueParsing, _parcJSONValue_FalseParser_Bad) +{ + char *string = "falsx"; + PARCBuffer *buffer = parcBuffer_WrapCString(string); + + PARCJSONParser *parser = parcJSONParser_Create(buffer); + PARCJSONValue *actual = _parcJSONValue_FalseParser(parser); + + assertNull(actual, "Expected a NULL return value"); + + parcJSONParser_Release(&parser); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(JSONValueParsing, _parcJSONValue_StringParser) +{ + char *parserInput = "\"\\\" \\\\ \\b \\f \\n \\r \\t \\/\""; + PARCBuffer *buffer = parcBuffer_WrapCString(parserInput); + PARCBuffer *expected = parcBuffer_AllocateCString("\" \\ \b \f \n \r \t /"); + + PARCJSONParser *parser = parcJSONParser_Create(buffer); + PARCJSONValue *actual = _parcJSONValue_StringParser(parser); + + assertTrue(parcJSONValue_IsString(actual), + "Expected a JSON String value."); + + assertTrue(parcBuffer_Equals(expected, actual->value.string), + "Expected '%s' actual '%s'", parcBuffer_ToString(expected), parcBuffer_ToString(actual->value.string)) + { + parcBuffer_Display(expected, 0); + parcBuffer_Display(actual->value.string, 0); + } + + char *string = parcJSONValue_ToString(actual); + assertTrue(strcmp(parserInput, string) == 0, + "Expected %s, actual %s", parserInput, string); + + parcMemory_Deallocate((void **) &string); + + parcJSONValue_Release(&actual); + parcJSONParser_Release(&parser); + parcBuffer_Release(&expected); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(JSONValueParsing, _parcJSONValue_StringParser_BAD) +{ + char *bad[] = { + "\"\t\"", + "\"", + NULL + }; + + for (int i = 0; bad[i] != NULL; i++) { + char *parserInput = bad[i]; + PARCBuffer *buffer = parcBuffer_WrapCString(parserInput); + PARCJSONParser *parser = parcJSONParser_Create(buffer); + PARCJSONValue *actual = _parcJSONValue_StringParser(parser); + + assertNull(actual, "Expected failure"); + parcBuffer_Release(&buffer); + parcJSONParser_Release(&parser); + } +} + +LONGBOW_TEST_CASE(JSONValueParsing, parcJSONValue_ObjectParser) +{ + char *string = "{ \"name\" : 1, \"name2\" : 2 }"; + PARCBuffer *buffer = parcBuffer_WrapCString(string); + + PARCJSONParser *parser = parcJSONParser_Create(buffer); + PARCJSONValue *actual = parcJSONValue_ObjectParser(parser); + + assertTrue(parcJSONValue_IsJSON(actual), "Expected a JSON Object value."); + + parcJSONValue_Release(&actual); + parcJSONParser_Release(&parser); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(JSONValueParsing, parcJSONValue_ObjectParser_Bad_Pair) +{ + char *string = "{ \"name\" , \"name2\" : 2 }"; + PARCBuffer *buffer = parcBuffer_WrapCString(string); + + PARCJSONParser *parser = parcJSONParser_Create(buffer); + PARCJSONValue *actual = parcJSONValue_ObjectParser(parser); + + assertNull(actual, "Expected parcJSONValue_ObjectParser to return NULL indicating failure"); + + parcJSONParser_Release(&parser); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(JSONValueParsing, parcJSONValue_ObjectParser_Bad_Pair2) +{ + char *string = "{ 2 }"; + PARCBuffer *buffer = parcBuffer_WrapCString(string); + + PARCJSONParser *parser = parcJSONParser_Create(buffer); + PARCJSONValue *actual = parcJSONValue_ObjectParser(parser); + + assertNull(actual, "Expected parcJSONValue_ObjectParser to return NULL indicating failure"); + + parcJSONParser_Release(&parser); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(JSONValueParsing, parcJSONValue_ArrayParser) +{ + char *string = "[ \"name\", 1, true, false, null, [ ], { } ]"; + PARCBuffer *buffer = parcBuffer_WrapCString(string); + + PARCJSONParser *parser = parcJSONParser_Create(buffer); + PARCJSONValue *actual = _parcJSONValue_ArrayParser(parser); + + assertTrue(parcJSONValue_IsArray(actual), "Expected a JSON Array value."); + + parcJSONValue_Release(&actual); + parcJSONParser_Release(&parser); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(JSONValueParsing, parcJSONValue_NumberParser_BatchedFloat) +{ + struct test_values { + char *string; + char *expectedString; + long double floatValue; + int64_t integerValue; + long double floatTolerance; + } successful[] = { + { "0", "0", 0.0, 0, 0 }, + { " 1", "1", 1.0, 1, 0 }, + { "-1", "-1", -1.0, -1, 0 }, + { "1e1", "1e1", 1.0e1, 10, 0 }, + { "-2e1", "-2e1", -2.0e1, -2e1, 0 }, + { "-2e+1", "-2e1", -2.0e+1, -2e+1, 0 }, + { " 1.0", "1", 1.0, 1, 0 }, + { "3e-1", "3e-1", 3e-1, 0, 0.01e-1 }, + { "100e-2", "100e-2", 100e-2, 100e-2, 0.0001 }, + { "123.456e11", "123.456e11", 123.456e11, 12345600000000, 0.0001e11 }, + { "-0.0415e-12", "-0.0415e-12", -0.0415e-12, 0, 0.00001e-12 }, + { "-0.0415e12", "-0.0415e12", -0.0415e12, -41500000000, 0.00001e12 }, + { "-0.0415", "-0.0415", -0.0415, 0, 0.00001 }, + { "-3.0415", "-3.0415", -3.0415, -3, 0.00001 }, + { "123.456", "123.456", 123.456, 123, 0.0001 }, + { "123.456e+11", "123.456e11", 123.456e+11, 12345600000000, 0.0001e+11 }, + { "123.456e-11", "123.456e-11", 123.456e-11, 0, 0.0001e-11 }, + { "1e-1", "1e-1", 1e-1, 0, 0.1e-1 }, + { NULL }, + }; + + for (int i = 0; successful[i].string != NULL; i++) { + PARCBuffer *buffer = parcBuffer_WrapCString(successful[i].string); + PARCJSONParser *parser = parcJSONParser_Create(buffer); + parcBuffer_Release(&buffer); + + PARCJSONValue *expected = _parcJSONValue_NumberParser(parser); + assertNotNull(expected, "_parcJSONValue_NumberParser returned NULL"); + + long double floatValue = parcJSONValue_GetFloat(expected); + + assertTrue(fabsl(floatValue - successful[i].floatValue) <= successful[i].floatTolerance, + "Expected %Lf actual %Lf", successful[i].floatValue, floatValue); + + char *string = parcJSONValue_ToString(expected); + assertTrue(strcmp(successful[i].expectedString, string) == 0, + "Expected %s actual %s", successful[i].expectedString, string); + parcMemory_Deallocate((void **) &string); + + int64_t integerValue = parcJSONValue_GetInteger(expected); + assertTrue(integerValue == (int64_t) successful[i].integerValue, + "Expected %" PRIi64 " actual %" PRIi64 "", (int64_t) successful[i].integerValue, integerValue); + + parcJSONValue_Release(&expected); + parcJSONParser_Release(&parser); + } +} + +LONGBOW_TEST_CASE(JSONValueParsing, parcJSONValue_Parser_Comma) +{ + char *string = ", null"; + PARCBuffer *buffer = parcBuffer_WrapCString(string); + + PARCJSONParser *parser = parcJSONParser_Create(buffer); + PARCJSONValue *actual = parcJSONValue_Parser(parser); + + assertNull(actual, "Expected parcJSONValue_Parser to return NULL when encountering a comma"); + + parcJSONParser_Release(&parser); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(JSONValueParsing, parcJSONValue_Parser_CloseBracket) +{ + char *string = "], null"; + PARCBuffer *buffer = parcBuffer_WrapCString(string); + + PARCJSONParser *parser = parcJSONParser_Create(buffer); + PARCJSONValue *actual = parcJSONValue_Parser(parser); + + assertNull(actual, "Expected parcJSONValue_Parser to return NULL when encountering a ]"); + + parcJSONParser_Release(&parser); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(JSONValueParsing, parcJSONValue_Parser_Null) +{ + char *string = " null"; + PARCBuffer *buffer = parcBuffer_WrapCString(string); + + PARCJSONParser *parser = parcJSONParser_Create(buffer); + PARCJSONValue *actual = parcJSONValue_Parser(parser); + + assertTrue(parcJSONValue_IsNull(actual), + "Expected parcJSONValue_Parser to return a Null JSON value when encountering 'null'"); + + parcJSONValue_Release(&actual); + parcJSONParser_Release(&parser); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(JSONValueParsing, parcJSONValue_Parser_True) +{ + char *string = " true"; + PARCBuffer *buffer = parcBuffer_WrapCString(string); + + PARCJSONParser *parser = parcJSONParser_Create(buffer); + PARCJSONValue *actual = parcJSONValue_Parser(parser); + + assertTrue(parcJSONValue_IsBoolean(actual), + "Expected parcJSONValue_Parser to return a boolean JSON value when encountering 'true'"); + assertTrue(parcJSONValue_GetBoolean(actual), + "Expected true"); + + parcJSONValue_Release(&actual); + parcJSONParser_Release(&parser); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(JSONValueParsing, parcJSONValue_Parser_False) +{ + char *string = " false"; + PARCBuffer *buffer = parcBuffer_WrapCString(string); + + PARCJSONParser *parser = parcJSONParser_Create(buffer); + PARCJSONValue *actual = parcJSONValue_Parser(parser); + + assertTrue(parcJSONValue_IsBoolean(actual), + "Expected parcJSONValue_Parser to return a boolean JSON value when encountering 'false'"); + assertFalse(parcJSONValue_GetBoolean(actual), + "Expected true"); + + parcJSONValue_Release(&actual); + parcJSONParser_Release(&parser); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(JSONValueParsing, parcJSONValue_Parser_String) +{ + char *string = " \"string\""; + PARCBuffer *buffer = parcBuffer_WrapCString(string); + + PARCJSONParser *parser = parcJSONParser_Create(buffer); + PARCJSONValue *actual = parcJSONValue_Parser(parser); + + assertTrue(parcJSONValue_IsString(actual), + "Expected parcJSONValue_Parser to return a string JSON value"); + + parcJSONValue_Release(&actual); + parcJSONParser_Release(&parser); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(JSONValueParsing, parcJSONValue_Parser_Array) +{ + char *string = " [ ]"; + PARCBuffer *buffer = parcBuffer_WrapCString(string); + + PARCJSONParser *parser = parcJSONParser_Create(buffer); + PARCJSONValue *value = parcJSONValue_Parser(parser); + + assertTrue(parcJSONValue_IsArray(value), + "Expected parcJSONValue_Parser to return a array JSON value"); + + PARCJSONArray *array = parcJSONValue_GetArray(value); + assertNotNull(array, "Expected a non-null pointer to a PARCJSONArray"); + + parcJSONValue_Release(&value); + parcJSONParser_Release(&parser); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(JSONValueParsing, parcJSONValue_Parser_Object) +{ + char *string = " { }"; + PARCBuffer *buffer = parcBuffer_WrapCString(string); + + PARCJSONParser *parser = parcJSONParser_Create(buffer); + PARCJSONValue *actual = parcJSONValue_Parser(parser); + + assertTrue(parcJSONValue_IsJSON(actual), + "Expected parcJSONValue_Parser to return a JSON object value"); + + parcJSONValue_Release(&actual); + parcJSONParser_Release(&parser); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_FIXTURE(Static) +{ + LONGBOW_RUN_TEST_CASE(Static, _parseSign_Negative); + LONGBOW_RUN_TEST_CASE(Static, _parseSign_NotASign); + LONGBOW_RUN_TEST_CASE(Static, _parseSign_Nil); + + LONGBOW_RUN_TEST_CASE(Static, _parseWholeNumber); + LONGBOW_RUN_TEST_CASE(Static, _parseOptionalFraction); + LONGBOW_RUN_TEST_CASE(Static, _parseOptionalExponent); +} + +LONGBOW_TEST_FIXTURE_SETUP(Static) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Static) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Static, _parseSign_Negative) +{ + char *string = "-"; + + PARCBuffer *buffer = parcBuffer_WrapCString(string); + PARCJSONParser *parser = parcJSONParser_Create(buffer); + parcBuffer_Release(&buffer); + int sign; + bool result = _parseSign(parser, &sign); + + assertTrue(result, "Expected true from _parseSign()"); + + parcJSONParser_Release(&parser); +} + +LONGBOW_TEST_CASE(Static, _parseSign_NotASign) +{ + char *string = "asd"; + + PARCBuffer *buffer = parcBuffer_WrapCString(string); + PARCJSONParser *parser = parcJSONParser_Create(buffer); + parcBuffer_Release(&buffer); + int sign; + bool result = _parseSign(parser, &sign); + + assertFalse(result, "Expected true from _parseSign()"); + + parcJSONParser_Release(&parser); +} + +LONGBOW_TEST_CASE(Static, _parseSign_Nil) +{ + char *string = ""; + + PARCBuffer *buffer = parcBuffer_WrapCString(string); + PARCJSONParser *parser = parcJSONParser_Create(buffer); + parcBuffer_Release(&buffer); + int sign; + bool result = _parseSign(parser, &sign); + + assertTrue(result, "Expected true from _parseSign()"); + + parcJSONParser_Release(&parser); +} + +LONGBOW_TEST_CASE(Static, _parseWholeNumber) +{ + struct test_values { + char *string; + long double value; + } successful[] = { + { "0", 0 }, + { "1", 1 }, + { "123", 123 }, + { NULL }, + }; + + for (int i = 0; successful[i].string != NULL; i++) { + PARCBuffer *buffer = parcBuffer_WrapCString(successful[i].string); + PARCJSONParser *parser = parcJSONParser_Create(buffer); + parcBuffer_Release(&buffer); + + int64_t value = 0; + + bool actual = _parseWholeNumber(parser, &value); + + assertTrue(actual, "Expected true from _parseNumber()"); + assertTrue(value == successful[i].value, + "Expected %Lf actual %" PRIi64 "", successful[i].value, value); + parcJSONParser_Release(&parser); + } +} + +LONGBOW_TEST_CASE(Static, _parseOptionalFraction) +{ + struct test_values { + char *string; + long double value; + bool correct; + } successful[] = { + { ".0", 0, true }, + { ".", 0, false }, + { ".1", 1, true }, + { "crap", 0, false }, + { "}", 0, true }, + { NULL }, + }; + + for (int i = 0; successful[i].string != NULL; i++) { + PARCBuffer *buffer = parcBuffer_WrapCString(successful[i].string); + PARCJSONParser *parser = parcJSONParser_Create(buffer); + parcBuffer_Release(&buffer); + + int64_t value = 0; + int log10OfFraction; + + bool actual = _parseOptionalFraction(parser, &value, &log10OfFraction); + + assertTrue(actual == successful[i].correct, "Expected true from _parseNumber()"); + assertTrue(value == successful[i].value, + "Expected %Lf actual %" PRIi64 "", successful[i].value, value); + parcJSONParser_Release(&parser); + } +} + +LONGBOW_TEST_CASE(Static, _parseOptionalExponent) +{ + struct test_values { + char *string; + long double value; + bool correct; + } successful[] = { + { "e", 0, false }, + { "ex", 0, false }, + { "e-1", -1, true }, + { "e1", 1, true }, + { "e+1", 1, true }, + { "x", 0, false }, + { NULL }, + }; + + for (int i = 0; successful[i].string != NULL; i++) { + PARCBuffer *buffer = parcBuffer_WrapCString(successful[i].string); + PARCJSONParser *parser = parcJSONParser_Create(buffer); + parcBuffer_Release(&buffer); + + int64_t value = 0; + + bool actual = _parseOptionalExponent(parser, &value); + + assertTrue(actual == successful[i].correct, "Expected true from _parseNumber()"); + assertTrue(value == successful[i].value, + "Expected %Lf actual %" PRIi64 "", successful[i].value, value); + parcJSONParser_Release(&parser); + } +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_JSONValue); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_KeyValue.c b/libparc/parc/algol/test/test_parc_KeyValue.c new file mode 100755 index 00000000..f9440602 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_KeyValue.c @@ -0,0 +1,386 @@ +/* + * 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 <LongBow/unit-test.h> + +#include <stdio.h> + +#include <parc/algol/parc_SafeMemory.h> +#include <parc/testing/parc_ObjectTesting.h> + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../parc_KeyValue.c" + + +LONGBOW_TEST_RUNNER(parc_KeyValue) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(PARCKeyValueAsPARCObject); + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_KeyValue) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_KeyValue) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestRunner_GetName(testRunner), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(PARCKeyValueAsPARCObject) +{ + LONGBOW_RUN_TEST_CASE(PARCKeyValueAsPARCObject, parcObject_Conformance); +} + +LONGBOW_TEST_FIXTURE_SETUP(PARCKeyValueAsPARCObject) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(PARCKeyValueAsPARCObject) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(PARCKeyValueAsPARCObject, parcObject_Conformance) +{ + PARCBuffer *key = parcBuffer_WrapCString("Key_1"); + PARCBuffer *value = parcBuffer_WrapCString("Value"); + PARCKeyValue *inst1 = parcKeyValue_Create(key, value); + PARCKeyValue *inst2 = parcKeyValue_Create(key, value); + PARCKeyValue *inst3 = parcKeyValue_Create(key, value); + parcBuffer_Release(&key); + parcBuffer_Release(&value); + + key = parcBuffer_WrapCString("Key_0"); + value = parcBuffer_WrapCString("Value"); + PARCKeyValue *lesser = parcKeyValue_Create(key, value); + parcBuffer_Release(&key); + parcBuffer_Release(&value); + + key = parcBuffer_WrapCString("Key_2"); + value = parcBuffer_WrapCString("Value"); + PARCKeyValue *greater = parcKeyValue_Create(key, value); + parcBuffer_Release(&key); + parcBuffer_Release(&value); + + parcObjectTesting_AssertObjectConformance(inst1, inst2, inst3, lesser, greater); + + parcKeyValue_Release(&inst1); + parcKeyValue_Release(&inst2); + parcKeyValue_Release(&inst3); + parcKeyValue_Release(&lesser); + parcKeyValue_Release(&greater); +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcKeyValue_Create); + LONGBOW_RUN_TEST_CASE(Global, parcKeyValue_Acquire); + LONGBOW_RUN_TEST_CASE(Global, parcKeyValue_Equals); + LONGBOW_RUN_TEST_CASE(Global, parcKeyValue_Compare); + LONGBOW_RUN_TEST_CASE(Global, parcKeyValue_HashCode); + LONGBOW_RUN_TEST_CASE(Global, parcKeyValue_Copy); + LONGBOW_RUN_TEST_CASE(Global, parcKeyValue_GetKey); + LONGBOW_RUN_TEST_CASE(Global, parcKeyValue_GetValue); + LONGBOW_RUN_TEST_CASE(Global, parcKeyValue_EqualKeys); + LONGBOW_RUN_TEST_CASE(Global, parcKeyValue_SetKey); + LONGBOW_RUN_TEST_CASE(Global, parcKeyValue_SetValue); +} + +typedef struct { + PARCKeyValue *testKV1; + PARCKeyValue *testKV2; + PARCKeyValue *nullValue; + PARCBuffer *key1; + PARCBuffer *value1; + PARCBuffer *key2; + PARCBuffer *value2; +} TestData; + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + + data->key1 = parcBuffer_WrapCString("This is key 1"); + data->value1 = parcBuffer_WrapCString("This is value 1"); + data->key2 = parcBuffer_WrapCString("This is key 2"); + data->value2 = parcBuffer_WrapCString("This is value 2"); + + data->testKV1 = parcKeyValue_Create(data->key1, data->value1); + data->testKV2 = parcKeyValue_Create(data->key2, data->value2); + + PARCBuffer *nullKey = parcBuffer_WrapCString("NULL KEY"); + data->nullValue = parcKeyValue_Create(nullKey, NULL); + parcBuffer_Release(&nullKey); + + longBowTestCase_SetClipBoardData(testCase, data); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + parcBuffer_Release(&data->key1); + parcBuffer_Release(&data->value1); + parcBuffer_Release(&data->key2); + parcBuffer_Release(&data->value2); + + parcKeyValue_Release(&data->testKV1); + parcKeyValue_Release(&data->testKV2); + parcKeyValue_Release(&data->nullValue); + + parcMemory_Deallocate((void **) &data); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcKeyValue_Create) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + assertNotNull(data->testKV1, "Expect a non-NULL key value"); +} + +LONGBOW_TEST_CASE(Global, parcKeyValue_Acquire) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + assertNotNull(data->testKV1, "Expect a non-NULL key value"); + + PARCKeyValue *kv = parcKeyValue_Acquire(data->testKV1); + parcKeyValue_Release(&kv); +} + +LONGBOW_TEST_CASE(Global, parcKeyValue_GetKey) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + assertTrue(parcBuffer_Equals(parcKeyValue_GetKey(data->testKV1), data->key1), + "The key returned is not the key provided"); + + assertNotNull(parcKeyValue_GetKey(data->nullValue), "Expect Non-NULL key from NULL value kv"); +} + +LONGBOW_TEST_CASE(Global, parcKeyValue_GetValue) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + assertTrue(parcBuffer_Equals(parcKeyValue_GetValue(data->testKV1), data->value1), + "The key returned is not the key provided"); + + assertNull(parcKeyValue_GetValue(data->nullValue), "Expect NULL from GetValue"); +} + +LONGBOW_TEST_CASE(Global, parcKeyValue_Equals) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + bool isEqual = parcKeyValue_Equals(data->testKV1, data->testKV2); + assertFalse(isEqual, "Expect test key-values to not be equal"); + + PARCKeyValue *kv = parcKeyValue_Create(data->key1, data->value2); + isEqual = parcKeyValue_Equals(kv, data->testKV1) || parcKeyValue_Equals(kv, data->testKV2); + parcKeyValue_Release(&kv); + assertFalse(isEqual, "Expect test key-values to not be equal"); + + kv = parcKeyValue_Create(data->key1, data->value1); + isEqual = parcKeyValue_Equals(kv, data->testKV1) && !parcKeyValue_Equals(kv, data->testKV2); + parcKeyValue_Release(&kv); + + assertTrue(isEqual, "Expect test key-values to be equal"); + + // NULL values + isEqual = parcKeyValue_Equals(data->testKV1, data->nullValue); + assertFalse(isEqual, "Expect NULL key-valuet to not be equal"); + + kv = parcKeyValue_Copy(data->nullValue); + isEqual = parcKeyValue_Equals(kv, data->nullValue); + assertTrue(isEqual, "Expect NULL key-valuet to not be equal"); + parcKeyValue_Release(&kv); +} + +LONGBOW_TEST_CASE(Global, parcKeyValue_Compare) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + assertTrue(parcKeyValue_Compare(NULL, NULL) == 0, "Expect 0 from comparing NULLs"); + assertTrue(parcKeyValue_Compare(data->testKV1, NULL) > 0, "Expect result > 0 from comparing non-NULL to NULL"); + assertTrue(parcKeyValue_Compare(NULL, data->testKV1) < 0, "Expect result < 0 from comparing NULL to non-NULL"); + + int result = parcKeyValue_Compare(data->testKV1, data->testKV2); + assertTrue(result < 0, "Expect compareison to be < 0"); + + result = parcKeyValue_Compare(data->testKV2, data->testKV1); + assertTrue(result > 0, "Expect compareison to be > 0"); + + // Mixed keys & values + PARCKeyValue *kv = parcKeyValue_Create(data->key1, data->value2); + result = parcKeyValue_Compare(kv, data->testKV1); + assertTrue(result == 0, "Expect comparison to be 0"); + + result = parcKeyValue_Compare(kv, data->testKV2); + assertTrue(result < 0, "Expect comparison to be < 0"); + + parcKeyValue_Release(&kv); + + // NULL value + result = parcKeyValue_Compare(data->testKV1, data->nullValue); + assertTrue(result > 0, "Expect NULL key-value be > 0"); +} + +LONGBOW_TEST_CASE(Global, parcKeyValue_HashCode) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCHashCode hash1 = parcKeyValue_HashCode(data->testKV1); + PARCHashCode hash2 = parcKeyValue_HashCode(data->testKV2); + assertFalse(hash1 == hash2, "Expect hash codes to be different"); + + PARCKeyValue *kv = parcKeyValue_Create(data->key1, data->value1); + hash2 = parcKeyValue_HashCode(kv); + assertTrue(hash1 == hash2, "Expect hash codes to be equal"); + parcKeyValue_Release(&kv); + + // Mixed keys & values + kv = parcKeyValue_Create(data->key1, data->value2); + hash2 = parcKeyValue_HashCode(kv); + assertTrue(hash1 == hash2, "Expect hash codes to be equal"); + parcKeyValue_Release(&kv); + + // NULL value + PARCHashCode hash = parcKeyValue_HashCode(data->nullValue); + assertTrue(hash != 0, "Expect NULL key-value hash to != 0"); +} + +LONGBOW_TEST_CASE(Global, parcKeyValue_EqualKeys) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + assertFalse(parcKeyValue_EqualKeys(data->testKV1, data->testKV2), "Expect keys to be different"); + + PARCKeyValue *kv = parcKeyValue_Create(data->key1, data->value2); + + assertTrue(parcKeyValue_EqualKeys(data->testKV1, kv), "Expect keys to be equal"); + + parcKeyValue_Release(&kv); + + // NULL value + assertFalse(parcKeyValue_EqualKeys(data->nullValue, data->testKV1), "Expect NULL key-value hash to != 0"); +} + +LONGBOW_TEST_CASE(Global, parcKeyValue_SetKey) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + parcKeyValue_SetKey(data->testKV2, data->key1); + + assertTrue(parcKeyValue_EqualKeys(data->testKV1, data->testKV2), + "Expect kv keys to be equal after SetKey"); + + // NULL value + parcKeyValue_SetKey(data->nullValue, data->key1); + assertTrue(parcKeyValue_EqualKeys(data->testKV1, data->nullValue), + "Expect kv keys to be equal after SetKey"); +} + +LONGBOW_TEST_CASE(Global, parcKeyValue_SetValue) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + parcKeyValue_SetValue(data->testKV2, data->value1); + + assertTrue(parcBuffer_Equals(parcKeyValue_GetValue(data->testKV1), + parcKeyValue_GetValue(data->testKV2)), + "Expect kv values to be equal after SetValue"); + + // NULL value + parcKeyValue_SetValue(data->testKV2, NULL); + assertNull(parcKeyValue_GetValue(data->testKV2), + "Expect NULL for testKV2 after SetValue"); + + parcKeyValue_SetValue(data->nullValue, data->value1); + assertTrue(parcBuffer_Equals(parcKeyValue_GetValue(data->testKV1), + parcKeyValue_GetValue(data->nullValue)), + "Expect kv values to be equal after SetValue"); +} + +LONGBOW_TEST_CASE(Global, parcKeyValue_Copy) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCKeyValue *kv = parcKeyValue_Copy(data->testKV1); + assertTrue(parcKeyValue_Equals(kv, data->testKV1), + "Expect key-value copy to be equal to original key-value"); + + parcKeyValue_Release(&kv); + + // NULL value + kv = parcKeyValue_Copy(data->nullValue); + assertTrue(parcKeyValue_Equals(kv, data->nullValue), + "Expect key-value copy to be equal to original key-value"); + + parcKeyValue_Release(&kv); +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_KeyValue); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_KeyedElement.c b/libparc/parc/algol/test/test_parc_KeyedElement.c new file mode 100755 index 00000000..a6866437 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_KeyedElement.c @@ -0,0 +1,134 @@ +/* + * 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 "../parc_KeyedElement.c" + +#include <LongBow/unit-test.h> +#include <stdio.h> + +#include <parc/algol/parc_SafeMemory.h> + +LONGBOW_TEST_RUNNER(parc_KeyedElement) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_KeyedElement) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_KeyedElement) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcKeyedElement_CreateDestroy); + LONGBOW_RUN_TEST_CASE(Global, parcKeyedElement_GetData); + LONGBOW_RUN_TEST_CASE(Global, parcKeyedElement_GetKey); + LONGBOW_RUN_TEST_CASE(Global, parcKeyedElement_SetData); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcKeyedElement_CreateDestroy) +{ + char *key1 = "key001"; + size_t keylen1 = 7; + PARCKeyedElement *keyedElement = parcKeyedElement_Create("Some data", key1, keylen1); + parcKeyedElement_Destroy(&keyedElement); +} + +LONGBOW_TEST_CASE(Global, parcKeyedElement_GetData) +{ + char *initial_data = "some data here"; + char *key1 = "key001"; + size_t keylen1 = 7; + PARCKeyedElement *keyedElement = parcKeyedElement_Create(initial_data, key1, keylen1); + char *data = parcKeyedElement_GetData(keyedElement); + assertTrue(initial_data == data, "We got different data from element"); + parcKeyedElement_Destroy(&keyedElement); +} + +LONGBOW_TEST_CASE(Global, parcKeyedElement_GetKey) +{ + char *initial_data = "some data here"; + char *key1 = "key001"; + size_t keylen1 = 7; + PARCKeyedElement *keyedElement = parcKeyedElement_Create(initial_data, key1, keylen1); + char *thekey = parcKeyedElement_GetKey(keyedElement); + size_t thekeylen = parcKeyedElement_GetKeyLen(keyedElement); + assertTrue(keylen1 == thekeylen, "We got different key size?"); + assertTrue(memcmp(key1, thekey, keylen1) == 0, "We got different keys?"); + parcKeyedElement_Destroy(&keyedElement); +} + +LONGBOW_TEST_CASE(Global, parcKeyedElement_SetData) +{ + char *key1 = "key001"; + size_t keylen1 = 7; + char *initial_data = "some data here"; + + PARCKeyedElement *keyedElement = parcKeyedElement_Create("Hello World", key1, keylen1); + + parcKeyedElement_SetData(keyedElement, initial_data); + char *data = parcKeyedElement_GetData(keyedElement); + assertTrue(initial_data == data, "We got different data from element"); + parcKeyedElement_Destroy(&keyedElement); +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_KeyedElement); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_LinkedList.c b/libparc/parc/algol/test/test_parc_LinkedList.c new file mode 100644 index 00000000..294f2ea2 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_LinkedList.c @@ -0,0 +1,1192 @@ +/* + * 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 "../parc_LinkedList.c" + +#include <LongBow/unit-test.h> +#include <parc/algol/parc_SafeMemory.h> +#include <parc/algol/parc_StdlibMemory.h> + +#include <parc/testing/parc_ObjectTesting.h> +#include <parc/testing/parc_MemoryTesting.h> + +LONGBOW_TEST_RUNNER(PARCLinkedList) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Local); + LONGBOW_RUN_TEST_FIXTURE(AcquireRelease); + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Performance); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(PARCLinkedList) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(PARCLinkedList) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(AcquireRelease) +{ + LONGBOW_RUN_TEST_CASE(AcquireRelease, parcLinkedList_CreateRelease); + LONGBOW_RUN_TEST_CASE(AcquireRelease, parcLinkedList_AcquireRelease); +} + +LONGBOW_TEST_FIXTURE_SETUP(AcquireRelease) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(AcquireRelease) +{ + bool leaked = parcMemoryTesting_ExpectedOutstanding(0, "%s leaks memory \n", longBowTestCase_GetName(testCase)) != true; + if (leaked) { + parcSafeMemory_ReportAllocation(STDOUT_FILENO); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(AcquireRelease, parcLinkedList_CreateRelease) +{ + PARCLinkedList *deque = parcLinkedList_Create(); + assertNotNull(deque, "Expected non-null result from parcLinkedList_Create()"); + + assertTrue(parcLinkedList_IsValid(deque), "Expected created PARCLinkedList to be valid."); + + parcLinkedList_Release(&deque); + assertNull(deque, "Expected parcLinkedList_Release to null the pointer"); +} + +LONGBOW_TEST_CASE(AcquireRelease, parcLinkedList_AcquireRelease) +{ + PARCLinkedList *original = parcLinkedList_Create(); + assertNotNull(original, "Expected non-null result from parcLinkedList_Create()"); + + parcObjectTesting_AssertAcquireReleaseContract(parcLinkedList_Acquire, original); + + PARCLinkedList *reference = parcLinkedList_Acquire(original); + assertTrue(original == reference, "Expected the reference to be equal to the original."); + + parcLinkedList_Release(&original); + assertNull(original, "Expected parcLinkedList_Release to null the pointer"); + + PARCBuffer *object = parcBuffer_Allocate(11); + parcLinkedList_Append(reference, object); + parcBuffer_Release(&object); + + size_t expected = 1; + size_t actual = parcLinkedList_Size(reference); + assertTrue(expected == actual, + "Expected size %zd, actual %zd", expected, actual); + parcLinkedList_Release(&reference); +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_AssertValid); + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_HashCode); + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_Append_One); + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_Append_Two); + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_AppendAll); + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_AppendAll_None); + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_CreateDestroy); + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_GetFirst); + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_GetLast); + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_SetAtIndex); + + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_Prepend_One); + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_Prepend_Two); + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_Prepend_Three); + + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_IsEmpty); + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_GetAtIndex); + + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_Contains_True); + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_Contains_False); + + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_RemoveFirst); + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_RemoveFirst_SingleElement); + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_RemoveLast); + + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_Remove); + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_RemoveNotFound); + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_RemoveAtIndex); + + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_Size); + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_Equals); + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_Copy); + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_InsertAtIndex_Head); + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_InsertAtIndex_HeadEmptyList); + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_InsertAtIndex_Tail); + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_InsertAtIndex_Middle); + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_Display); + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_Display_NULL); + + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_CreateIterator); + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_CreateIterator_Remove); + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_CreateIterator_RemoveHead); + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_CreateIterator_RemoveMiddle); + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_CreateIterator_RemoveTail); + + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_SetEquals_True); + LONGBOW_RUN_TEST_CASE(Global, parcLinkedList_SetEquals_False); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + bool leaked = parcMemoryTesting_ExpectedOutstanding(0, "%s leaks memory \n", longBowTestCase_GetName(testCase)) != true; + if (leaked) { + parcSafeMemory_ReportAllocation(STDOUT_FILENO); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_AssertValid) +{ + PARCLinkedList *list = parcLinkedList_Create(); + + parcLinkedList_AssertValid(list); + parcLinkedList_Release(&list); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_Append_One) +{ + PARCLinkedList *list = parcLinkedList_Create(); + + PARCBuffer *object = parcBuffer_Allocate(11); + PARCLinkedList *actual = parcLinkedList_Append(list, object); + parcBuffer_Release(&object); + + assertTrue(parcLinkedList_IsValid(list), "PARCLinkedList is invalid."); + + assertTrue(list == actual, "Expected parcLinkedList_Append to return its argument."); + assertTrue(parcLinkedList_Size(list) == 1, "Expected size of 1, actual %zd", parcLinkedList_Size(list)); + + parcLinkedList_Release(&list); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_Append_Two) +{ + PARCLinkedList *deque = parcLinkedList_Create(); + + PARCBuffer *object1 = parcBuffer_WrapCString("1"); + PARCBuffer *object2 = parcBuffer_WrapCString("2"); + + parcLinkedList_Append(deque, object1); + PARCLinkedList *actual = parcLinkedList_Append(deque, object2); + parcBuffer_Release(&object1); + parcBuffer_Release(&object2); + + assertTrue(deque == actual, "Expected parcLinkedList_Append to return its argument."); + assertTrue(parcLinkedList_Size(deque) == 2, "Expected size of 2, actual %zd", parcLinkedList_Size(deque)); + + parcLinkedList_Release(&deque); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_AppendAll) +{ + PARCLinkedList *other = parcLinkedList_Create(); + + for (int i = 0; i < 1000; i++) { + PARCBuffer *buffer = parcBuffer_PutUint32(parcBuffer_Allocate(sizeof(int)), i); + parcLinkedList_Append(other, buffer); + parcBuffer_Release(&buffer); + } + + PARCLinkedList *list = parcLinkedList_Create(); + + parcLinkedList_AppendAll(list, other); + + assertTrue(parcLinkedList_Equals(list, other), "Expected equal lists."); + + parcLinkedList_Release(&list); + parcLinkedList_Release(&other); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_AppendAll_None) +{ + PARCLinkedList *other = parcLinkedList_Create(); + + PARCLinkedList *list = parcLinkedList_Create(); + + parcLinkedList_AppendAll(list, other); + + assertTrue(parcLinkedList_Equals(list, other), "Expected equal lists."); + + parcLinkedList_Release(&list); + parcLinkedList_Release(&other); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_CreateDestroy) +{ + PARCLinkedList *deque = parcLinkedList_Create(); + assertNotNull(deque, "Expected non-null result from parcLinkedList_Create()"); + + parcLinkedList_Release(&deque); + assertNull(deque, "Expected parcLinkedList_Destroy to null the pointer"); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_HashCode) +{ + PARCBuffer *object1 = parcBuffer_WrapCString("1"); + PARCBuffer *object2 = parcBuffer_WrapCString("2"); + PARCBuffer *object3 = parcBuffer_WrapCString("3"); + + PARCLinkedList *deque = parcLinkedList_Create(); + parcLinkedList_Append(deque, object1); + parcLinkedList_Append(deque, object2); + parcLinkedList_Append(deque, object3); + + parcLinkedList_HashCode(deque); + + parcBuffer_Release(&object1); + parcBuffer_Release(&object2); + parcBuffer_Release(&object3); + parcLinkedList_Release(&deque); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_GetFirst) +{ + PARCBuffer *object1 = parcBuffer_WrapCString("1"); + PARCBuffer *object2 = parcBuffer_WrapCString("2"); + PARCBuffer *object3 = parcBuffer_WrapCString("3"); + + PARCLinkedList *deque = parcLinkedList_Create(); + parcLinkedList_Append(deque, object1); + parcLinkedList_Append(deque, object2); + parcLinkedList_Append(deque, object3); + + PARCBuffer *actual = parcLinkedList_GetFirst(deque); + assertTrue(parcBuffer_Equals(object1, actual), "Order of objects in the list is wrong."); + + parcBuffer_Release(&object1); + parcBuffer_Release(&object2); + parcBuffer_Release(&object3); + parcLinkedList_Release(&deque); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_GetLast) +{ + PARCBuffer *object1 = parcBuffer_WrapCString("1"); + PARCBuffer *object2 = parcBuffer_WrapCString("2"); + PARCBuffer *object3 = parcBuffer_WrapCString("3"); + + PARCLinkedList *deque = parcLinkedList_Create(); + parcLinkedList_Append(deque, object1); + parcLinkedList_Append(deque, object2); + parcLinkedList_Append(deque, object3); + + PARCBuffer *actual = parcLinkedList_GetLast(deque); + assertTrue(parcBuffer_Equals(object3, actual), "Order of objects in the list is wrong."); + + parcBuffer_Release(&object1); + parcBuffer_Release(&object2); + parcBuffer_Release(&object3); + parcLinkedList_Release(&deque); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_Prepend_One) +{ + PARCLinkedList *deque = parcLinkedList_Create(); + PARCBuffer *object1 = parcBuffer_WrapCString("1"); + PARCLinkedList *actual = parcLinkedList_Prepend(deque, object1); + parcBuffer_Release(&object1); + + assertTrue(deque == actual, "Expected parcLinkedList_Append to return its argument."); + assertTrue(parcLinkedList_Size(deque) == 1, "Expected size of 1, actual %zd", parcLinkedList_Size(deque)); + assertTrue(deque->head != NULL, "Expected head to be not null."); + assertTrue(deque->head == deque->tail, "Expected head to be equal to the tail."); + + parcLinkedList_Release(&deque); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_Prepend_Two) +{ + PARCLinkedList *deque = parcLinkedList_Create(); + + PARCBuffer *object1 = parcBuffer_WrapCString("1"); + PARCLinkedList *actual = parcLinkedList_Prepend(deque, object1); + parcLinkedList_Prepend(deque, object1); + parcBuffer_Release(&object1); + + assertTrue(deque == actual, "Expected parcLinkedList_Prepend to return its argument."); + assertTrue(parcLinkedList_Size(deque) == 2, "Expected size of 2, actual %zd", parcLinkedList_Size(deque)); + + parcLinkedList_Release(&deque); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_Prepend_Three) +{ + PARCLinkedList *deque = parcLinkedList_Create(); + + PARCBuffer *object1 = parcBuffer_WrapCString("1"); + PARCBuffer *object2 = parcBuffer_WrapCString("2"); + PARCBuffer *object3 = parcBuffer_WrapCString("3"); + parcLinkedList_Prepend(deque, object1); + parcLinkedList_Prepend(deque, object2); + PARCLinkedList *actual = parcLinkedList_Prepend(deque, object3); + + assertTrue(deque == actual, "Expected parcLinkedList_Prepend to return its argument."); + assertTrue(parcLinkedList_Size(deque) == 3, "Expected size of 3, actual %zd", parcLinkedList_Size(deque)); + + PARCBuffer *peek = parcLinkedList_GetFirst(deque); + assertTrue(parcBuffer_Equals(object3, peek), "Order of objects failed"); + + peek = parcLinkedList_GetLast(deque); + assertTrue(parcBuffer_Equals(object1, peek), "Order of objects failed"); + + parcBuffer_Release(&object1); + parcBuffer_Release(&object2); + parcBuffer_Release(&object3); + + parcLinkedList_Release(&deque); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_RemoveFirst) +{ + PARCLinkedList *list = parcLinkedList_Create(); + + for (int i = 0; i < 1000; i++) { + PARCBuffer *buffer = parcBuffer_Flip(parcBuffer_PutUint32(parcBuffer_Allocate(sizeof(int)), i)); + parcLinkedList_Append(list, buffer); + parcBuffer_Release(&buffer); + } + + PARCBuffer *peek = parcLinkedList_RemoveFirst(list); + assertTrue(parcObject_GetReferenceCount(peek) == 1, "Expected reference count to be 1."); + assertTrue(parcBuffer_GetUint32(peek) == 0, "Objects out of order."); + + parcBuffer_Release(&peek); + parcLinkedList_Release(&list); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_RemoveFirst_SingleElement) +{ + PARCBuffer *object1 = parcBuffer_WrapCString("1"); + PARCLinkedList *deque = parcLinkedList_Create(); + parcLinkedList_Prepend(deque, object1); + + PARCBuffer *peek = parcLinkedList_RemoveFirst(deque); + assertTrue(parcBuffer_Equals(object1, peek), + "Objects out of order."); + + parcBuffer_Release(&peek); + parcBuffer_Release(&object1); + parcLinkedList_Release(&deque); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_RemoveLast) +{ + PARCLinkedList *list = parcLinkedList_Create(); + + for (int i = 0; i < 1000; i++) { + PARCBuffer *buffer = parcBuffer_Flip(parcBuffer_PutUint32(parcBuffer_Allocate(sizeof(int)), i)); + parcLinkedList_Append(list, buffer); + parcBuffer_Release(&buffer); + } + + PARCBuffer *peek = parcLinkedList_RemoveLast(list); + assertTrue(parcObject_GetReferenceCount(peek) == 1, "Expected reference count to be 1."); + assertTrue(parcBuffer_GetUint32(peek) == 999, "Objects out of order."); + + parcBuffer_Release(&peek); + parcLinkedList_Release(&list); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_RemoveLast_SingleElement) +{ + PARCBuffer *object1 = parcBuffer_WrapCString("1"); + + PARCLinkedList *deque = parcLinkedList_Create(); + parcLinkedList_Prepend(deque, object1); + + PARCBuffer *peek = parcLinkedList_RemoveLast(deque); + assertTrue(parcBuffer_Equals(object1, peek), + "Objects out of order."); + + parcBuffer_Release(&object1); + parcLinkedList_Release(&deque); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_Remove) +{ + PARCLinkedList *deque = parcLinkedList_Create(); + + PARCBuffer *object1 = parcBuffer_WrapCString("1"); + PARCBuffer *object2 = parcBuffer_WrapCString("2"); + PARCBuffer *object3 = parcBuffer_WrapCString("3"); + + parcLinkedList_Prepend(deque, object3); + parcLinkedList_Prepend(deque, object2); + parcLinkedList_Prepend(deque, object1); + + bool found = parcLinkedList_Remove(deque, object2); + assertTrue(found, "Expected item to be found"); + assertTrue(parcLinkedList_Size(deque) == 2, "Expected size of 2, actual %zd", parcLinkedList_Size(deque)); + + PARCBuffer *peek; + peek = parcLinkedList_RemoveFirst(deque); + assertTrue(parcBuffer_Equals(object1, peek), "Object1 was not first in list"); + parcBuffer_Release(&peek); + + peek = parcLinkedList_RemoveFirst(deque); + assertTrue(parcBuffer_Equals(object3, peek), "Object3 was not second in list"); + parcBuffer_Release(&peek); + + parcBuffer_Release(&object1); + parcBuffer_Release(&object2); + parcBuffer_Release(&object3); + parcLinkedList_Release(&deque); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_RemoveAtIndex) +{ + PARCLinkedList *list = parcLinkedList_Create(); + + PARCBuffer *object1 = parcBuffer_WrapCString("1"); + PARCBuffer *object2 = parcBuffer_WrapCString("2"); + PARCBuffer *object3 = parcBuffer_WrapCString("3"); + + parcLinkedList_Prepend(list, object3); + parcLinkedList_Prepend(list, object2); + parcLinkedList_Prepend(list, object1); + + PARCBuffer *actual = parcLinkedList_RemoveAtIndex(list, 1); + + assertTrue(parcBuffer_Equals(object2, actual), "Wrong object returned from parcLinkedList_RemoveAtIndex"); + + parcBuffer_Release(&object1); + parcBuffer_Release(&object2); + parcBuffer_Release(&object3); + parcBuffer_Release(&actual); + parcLinkedList_Release(&list); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_RemoveNotFound) +{ + PARCLinkedList *deque = parcLinkedList_Create(); + + PARCBuffer *object1 = parcBuffer_WrapCString("1"); + PARCBuffer *object2 = parcBuffer_WrapCString("2"); + PARCBuffer *object3 = parcBuffer_WrapCString("3"); + PARCBuffer *object4 = parcBuffer_WrapCString("4"); + + parcLinkedList_Prepend(deque, object3); + parcLinkedList_Prepend(deque, object2); + parcLinkedList_Prepend(deque, object1); + + bool found = parcLinkedList_Remove(deque, object4); + assertFalse(found, "Expected item to be not found"); + assertTrue(parcLinkedList_Size(deque) == 3, "Expected size of 3, actual %zd", parcLinkedList_Size(deque)); + + parcBuffer_Release(&object1); + parcBuffer_Release(&object2); + parcBuffer_Release(&object3); + parcBuffer_Release(&object4); + parcLinkedList_Release(&deque); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_Size) +{ + PARCBuffer *object1 = parcBuffer_WrapCString("1"); + PARCBuffer *object2 = parcBuffer_WrapCString("2"); + PARCBuffer *object3 = parcBuffer_WrapCString("3"); + + PARCLinkedList *deque = parcLinkedList_Create(); + parcLinkedList_Prepend(deque, object1); + parcLinkedList_Prepend(deque, object2); + parcLinkedList_Prepend(deque, object3); + + assertTrue(parcLinkedList_Size(deque) == 3, + "Expected 3, actual %zd", parcLinkedList_Size(deque)); + + parcBuffer_Release(&object1); + parcBuffer_Release(&object2); + parcBuffer_Release(&object3); + + parcLinkedList_Release(&deque); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_IsEmpty) +{ + PARCBuffer *object1 = parcBuffer_WrapCString("1"); + + PARCLinkedList *deque = parcLinkedList_Create(); + + assertTrue(parcLinkedList_IsEmpty(deque), "Expected true."); + parcLinkedList_Prepend(deque, object1); + assertFalse(parcLinkedList_IsEmpty(deque), "Expected false."); + + parcBuffer_Release(&object1); + parcLinkedList_Release(&deque); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_GetAtIndex) +{ + PARCBuffer *object1 = parcBuffer_WrapCString("1"); + PARCBuffer *object2 = parcBuffer_WrapCString("2"); + PARCBuffer *object3 = parcBuffer_WrapCString("3"); + + PARCLinkedList *deque = parcLinkedList_Create(); + parcLinkedList_Append(deque, object1); + parcLinkedList_Append(deque, object2); + parcLinkedList_Append(deque, object3); + + PARCBuffer *actual; + actual = parcLinkedList_GetAtIndex(deque, 0); + assertTrue(parcBuffer_Equals(actual, object1), "parcLinkedList_GetAtIndex failed"); + actual = parcLinkedList_GetAtIndex(deque, 1); + assertTrue(parcBuffer_Equals(actual, object2), "parcLinkedList_GetAtIndex failed"); + actual = parcLinkedList_GetAtIndex(deque, 2); + assertTrue(parcBuffer_Equals(actual, object3), "parcLinkedList_GetAtIndex failed"); + + parcBuffer_Release(&object1); + parcBuffer_Release(&object2); + parcBuffer_Release(&object3); + parcLinkedList_Release(&deque); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_SetAtIndex) +{ + PARCBuffer *object1 = parcBuffer_WrapCString("1"); + PARCBuffer *object2 = parcBuffer_WrapCString("2"); + PARCBuffer *object3 = parcBuffer_WrapCString("3"); + + PARCLinkedList *deque = parcLinkedList_Create(); + parcLinkedList_Append(deque, object1); + parcLinkedList_Append(deque, object2); + parcLinkedList_Append(deque, object3); + + PARCBuffer *newObject = parcBuffer_WrapCString("Hello"); + + PARCBuffer *actual = parcLinkedList_SetAtIndex(deque, 0, newObject); + assertTrue(parcBuffer_Equals(actual, object1), "parcLinkedList_SetAtIndex failed to return the old value."); + parcBuffer_Release(&actual); + + actual = parcLinkedList_GetAtIndex(deque, 0); + assertTrue(parcBuffer_Equals(actual, newObject), "parcLinkedList_SetAtIndex failed to set the new value."); + + parcBuffer_Release(&newObject); + parcBuffer_Release(&object1); + parcBuffer_Release(&object2); + parcBuffer_Release(&object3); + parcLinkedList_Release(&deque); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_Contains_True) +{ + PARCBuffer *object1 = parcBuffer_WrapCString("1"); + PARCBuffer *object2 = parcBuffer_WrapCString("2"); + PARCBuffer *object3 = parcBuffer_WrapCString("3"); + + PARCLinkedList *list = parcLinkedList_Create(); + parcLinkedList_Append(list, object1); + parcLinkedList_Append(list, object2); + parcLinkedList_Append(list, object3); + + bool actual = parcLinkedList_Contains(list, object2); + assertTrue(actual, "Expected parcLinkedList_Contains to return true for object in the list"); + + parcBuffer_Release(&object1); + parcBuffer_Release(&object2); + parcBuffer_Release(&object3); + parcLinkedList_Release(&list); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_Contains_False) +{ + PARCBuffer *object1 = parcBuffer_WrapCString("1"); + PARCBuffer *object2 = parcBuffer_WrapCString("2"); + PARCBuffer *object3 = parcBuffer_WrapCString("3"); + + PARCLinkedList *list = parcLinkedList_Create(); + parcLinkedList_Append(list, object1); + parcLinkedList_Append(list, object3); + + bool actual = parcLinkedList_Contains(list, object2); + assertFalse(actual, "Expected parcLinkedList_Contains to return false for object not in the list"); + + parcBuffer_Release(&object1); + parcBuffer_Release(&object2); + parcBuffer_Release(&object3); + parcLinkedList_Release(&list); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_Equals) +{ + PARCBuffer *object1 = parcBuffer_WrapCString("1"); + PARCBuffer *object2 = parcBuffer_WrapCString("2"); + PARCBuffer *object3 = parcBuffer_WrapCString("3"); + + PARCLinkedList *x = parcLinkedList_Create(); + parcLinkedList_Append(x, object1); + parcLinkedList_Append(x, object2); + PARCLinkedList *y = parcLinkedList_Create(); + parcLinkedList_Append(y, object1); + parcLinkedList_Append(y, object2); + PARCLinkedList *z = parcLinkedList_Create(); + parcLinkedList_Append(z, object1); + parcLinkedList_Append(z, object2); + PARCLinkedList *u1 = parcLinkedList_Create(); + parcLinkedList_Append(u1, object2); + PARCLinkedList *u2 = parcLinkedList_Create(); + parcLinkedList_Append(u2, object2); + parcLinkedList_Append(u2, object3); + + parcObjectTesting_AssertEqualsFunction(parcLinkedList_Equals, x, y, z, u1, u2, NULL); + + parcLinkedList_Release(&x); + parcLinkedList_Release(&y); + parcLinkedList_Release(&z); + parcLinkedList_Release(&u1); + parcLinkedList_Release(&u2); + + parcBuffer_Release(&object1); + parcBuffer_Release(&object2); + parcBuffer_Release(&object3); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_Copy) +{ + PARCLinkedList *x = parcLinkedList_Create(); + PARCBuffer *object1 = parcBuffer_WrapCString("1"); + PARCBuffer *object2 = parcBuffer_WrapCString("2"); + PARCBuffer *object3 = parcBuffer_WrapCString("3"); + parcLinkedList_Append(x, object1); + parcLinkedList_Append(x, object2); + parcLinkedList_Append(x, object3); + + PARCLinkedList *y = parcLinkedList_Copy(x); + + assertTrue(parcLinkedList_Equals(x, y), "Expected the copy to be equal to the original."); + + assertTrue(parcLinkedList_IsValid(x), "PARCLinkedList is invalid."); + + parcBuffer_Release(&object1); + parcBuffer_Release(&object2); + parcBuffer_Release(&object3); + + parcLinkedList_Release(&y); + parcLinkedList_Release(&x); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_InsertAtIndex_Head) +{ + PARCLinkedList *x = parcLinkedList_Create(); + PARCBuffer *object1 = parcBuffer_WrapCString("1"); + PARCBuffer *object2 = parcBuffer_WrapCString("2"); + PARCBuffer *object3 = parcBuffer_WrapCString("3"); + PARCBuffer *object4 = parcBuffer_WrapCString("4"); + parcLinkedList_Append(x, object1); + parcLinkedList_Append(x, object2); + parcLinkedList_Append(x, object3); + + parcLinkedList_InsertAtIndex(x, 0, object4); + + PARCBuffer *actual = parcLinkedList_GetAtIndex(x, 0); + + assertTrue(actual == object4, "Unexpected object at index 0"); + + assertTrue(parcLinkedList_IsValid(x), "PARCLinkedList is invalid."); + + parcBuffer_Release(&object1); + parcBuffer_Release(&object2); + parcBuffer_Release(&object3); + parcBuffer_Release(&object4); + + parcLinkedList_Release(&x); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_InsertAtIndex_HeadEmptyList) +{ + PARCLinkedList *x = parcLinkedList_Create(); + PARCBuffer *object1 = parcBuffer_WrapCString("1"); + PARCBuffer *object2 = parcBuffer_WrapCString("2"); + PARCBuffer *object3 = parcBuffer_WrapCString("3"); + PARCBuffer *object4 = parcBuffer_WrapCString("4"); + + parcLinkedList_InsertAtIndex(x, 0, object4); + assertTrue(x->head->object == object4, "Malformed linked list node does not contain the proper object reference"); + assertTrue(x->head == x->tail, "Expected the list head and tail to be the same for a single element list."); + assertTrue(parcLinkedList_IsValid(x), "PARCLinkedList is invalid."); + + PARCBuffer *actual = parcLinkedList_GetAtIndex(x, 0); + + assertTrue(actual == object4, "Unexpected object at index 0"); + + parcBuffer_Release(&object1); + parcBuffer_Release(&object2); + parcBuffer_Release(&object3); + parcBuffer_Release(&object4); + + parcLinkedList_Release(&x); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_InsertAtIndex_Tail) +{ + PARCLinkedList *x = parcLinkedList_Create(); + PARCBuffer *object1 = parcBuffer_WrapCString("1"); + PARCBuffer *object2 = parcBuffer_WrapCString("2"); + PARCBuffer *object3 = parcBuffer_WrapCString("3"); + PARCBuffer *object4 = parcBuffer_WrapCString("4"); + parcLinkedList_Append(x, object1); + parcLinkedList_Append(x, object2); + parcLinkedList_Append(x, object3); + + parcLinkedList_InsertAtIndex(x, 3, object4); + assertTrue(parcLinkedList_IsValid(x), "PARCLinkedList is invalid."); + + PARCBuffer *actual = parcLinkedList_GetAtIndex(x, 3); + + assertTrue(actual == object4, "Unexpected object at index 3"); + + parcBuffer_Release(&object1); + parcBuffer_Release(&object2); + parcBuffer_Release(&object3); + parcBuffer_Release(&object4); + + parcLinkedList_Release(&x); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_InsertAtIndex_Middle) +{ + PARCLinkedList *x = parcLinkedList_Create(); + PARCBuffer *object1 = parcBuffer_WrapCString("1"); + PARCBuffer *object2 = parcBuffer_WrapCString("2"); + PARCBuffer *object3 = parcBuffer_WrapCString("3"); + PARCBuffer *object4 = parcBuffer_WrapCString("4"); + parcLinkedList_Append(x, object1); + parcLinkedList_Append(x, object2); + parcLinkedList_Append(x, object3); + + parcLinkedList_InsertAtIndex(x, 1, object4); + assertTrue(parcLinkedList_IsValid(x), "PARCLinkedList is invalid."); + + assertTrue(parcLinkedList_GetAtIndex(x, 0) == object1, "Unexpected object at index 1"); + assertTrue(parcLinkedList_GetAtIndex(x, 1) == object4, "Unexpected object at index 1"); + assertTrue(parcLinkedList_GetAtIndex(x, 2) == object2, "Unexpected object at index 1"); + assertTrue(parcLinkedList_GetAtIndex(x, 3) == object3, "Unexpected object at index 1"); + + parcBuffer_Release(&object1); + parcBuffer_Release(&object2); + parcBuffer_Release(&object3); + parcBuffer_Release(&object4); + + parcLinkedList_Release(&x); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_Display) +{ + PARCLinkedList *x = parcLinkedList_Create(); + PARCBuffer *object1 = parcBuffer_WrapCString("1"); + PARCBuffer *object2 = parcBuffer_WrapCString("2"); + PARCBuffer *object3 = parcBuffer_WrapCString("3"); + parcLinkedList_Append(x, object1); + parcLinkedList_Append(x, object2); + parcLinkedList_Append(x, object3); + + parcLinkedList_Display(x, 0); + + parcBuffer_Release(&object1); + parcBuffer_Release(&object2); + parcBuffer_Release(&object3); + parcLinkedList_Release(&x); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_Display_NULL) +{ + parcLinkedList_Display(NULL, 0); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_CreateIterator) +{ + PARCLinkedList *x = parcLinkedList_Create(); + + uint32_t expectedCount = 10; + for (uint32_t i = 0; i < expectedCount; i++) { + PARCBuffer *object = parcBuffer_Allocate(sizeof(int)); + parcBuffer_PutUint32(object, i); + parcBuffer_Flip(object); + parcLinkedList_Append(x, object); + parcBuffer_Release(&object); + } + + PARCIterator *iterator = parcLinkedList_CreateIterator(x); + uint32_t expected = 0; + while (parcIterator_HasNext(iterator)) { + PARCBuffer *buffer = (PARCBuffer *) parcIterator_Next(iterator); + uint32_t actual = parcBuffer_GetUint32(buffer); + assertTrue(expected == actual, "Expected %d, actual %d", expected, actual); + expected++; + } + parcIterator_Release(&iterator); + + parcLinkedList_Release(&x); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_CreateIterator_Remove) +{ + PARCLinkedList *x = parcLinkedList_Create(); + for (size_t i = 0; i < 5; i++) { + PARCBuffer *buffer = parcBuffer_Allocate(10); + parcBuffer_PutUint64(buffer, i); + parcBuffer_Flip(buffer); + parcLinkedList_Append(x, buffer); + parcBuffer_Release(&buffer); + } + + assertTrue(parcLinkedList_IsValid(x), "PARCLinkedList is invalid."); + + PARCIterator *iterator = parcLinkedList_CreateIterator(x); + size_t expected = 0; + while (parcIterator_HasNext(iterator)) { + size_t actual = parcBuffer_GetUint64(parcIterator_Next(iterator)); + assertTrue(expected == actual, "Expected %zd, actual %zd", expected, actual); + parcIterator_Remove(iterator); + expected++; + } + parcIterator_Release(&iterator); + + iterator = parcLinkedList_CreateIterator(x); + assertFalse(parcIterator_HasNext(iterator), "Expected an interator on an empty list to not HaveNext"); + parcIterator_Release(&iterator); + + assertTrue(parcLinkedList_Size(x) == 0, "List is not empty."); + + assertTrue(parcLinkedList_IsValid(x), "PARCLinkedList is invalid."); + parcLinkedList_Release(&x); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_CreateIterator_RemoveHead) +{ + size_t listSize = 5; + + PARCLinkedList *x = parcLinkedList_Create(); + for (size_t i = 0; i < listSize; i++) { + PARCBuffer *buffer = parcBuffer_Allocate(10); + parcBuffer_PutUint64(buffer, i); + parcBuffer_Flip(buffer); + parcLinkedList_Append(x, buffer); + parcBuffer_Release(&buffer); + } + + assertTrue(parcLinkedList_IsValid(x), "PARCLinkedList is invalid."); + parcLinkedList_Display(x, 0); + + PARCIterator *iterator = parcLinkedList_CreateIterator(x); + if (parcIterator_HasNext(iterator)) { + PARCBuffer *buffer = (PARCBuffer *) parcIterator_Next(iterator); + size_t actual = parcBuffer_GetUint64(buffer); + assertTrue(actual == 0, "Expected %d, actual %zd", 0, actual); + parcIterator_Remove(iterator); + } + parcIterator_Release(&iterator); + + iterator = parcLinkedList_CreateIterator(x); + assertTrue(parcIterator_HasNext(iterator), "Expected an interator on a non-empty list to HaveNext"); + parcIterator_Release(&iterator); + + assertTrue(parcLinkedList_Size(x) == listSize - 1, "Expected the list to be %zd, actual %zd", listSize - 1, parcLinkedList_Size(x)); + + assertTrue(parcLinkedList_IsValid(x), "PARCLinkedList is invalid."); + parcLinkedList_Release(&x); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_CreateIterator_RemoveMiddle) +{ + size_t listSize = 5; + + PARCLinkedList *x = parcLinkedList_Create(); + for (size_t i = 0; i < listSize; i++) { + PARCBuffer *buffer = parcBuffer_Allocate(10); + parcBuffer_PutUint64(buffer, i); + parcBuffer_Flip(buffer); + parcLinkedList_Append(x, buffer); + parcBuffer_Release(&buffer); + } + + assertTrue(parcLinkedList_IsValid(x), "PARCLinkedList is invalid."); + assertTrue(parcLinkedList_Size(x) == listSize, "Expected the list to be %zd, actual %zd", listSize, parcLinkedList_Size(x)); + + + PARCIterator *iterator = parcLinkedList_CreateIterator(x); + for (size_t i = 0; i <= listSize / 2; i++) { + if (parcIterator_HasNext(iterator)) { + parcIterator_Next(iterator); + } + } + parcIterator_Remove(iterator); + + parcIterator_Release(&iterator); + + + iterator = parcLinkedList_CreateIterator(x); + size_t expected = 0; + while (parcIterator_HasNext(iterator)) { + if (expected != (listSize / 2)) { + size_t actual = parcBuffer_GetUint64(parcIterator_Next(iterator)); + assertTrue(expected == actual, "Expected %zd, actual %zd", expected, actual); + } + expected++; + } + parcIterator_Release(&iterator); + + assertTrue(parcLinkedList_Size(x) == listSize - 1, "Expected the list to be %zd, actual %zd", listSize - 1, parcLinkedList_Size(x)); + + assertTrue(parcLinkedList_IsValid(x), "PARCLinkedList is invalid."); + parcLinkedList_Release(&x); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_CreateIterator_RemoveTail) +{ + size_t listSize = 5; + + PARCLinkedList *x = parcLinkedList_Create(); + for (size_t i = 0; i < listSize; i++) { + PARCBuffer *buffer = parcBuffer_Allocate(10); + parcBuffer_PutUint64(buffer, i); + parcBuffer_Flip(buffer); + parcLinkedList_Append(x, buffer); + parcBuffer_Release(&buffer); + } + + assertTrue(parcLinkedList_IsValid(x), "PARCLinkedList is invalid."); + assertTrue(parcLinkedList_Size(x) == listSize, "Expected the list to be %zd, actual %zd", listSize, parcLinkedList_Size(x)); + + PARCIterator *iterator = parcLinkedList_CreateIterator(x); + for (size_t i = 0; i < listSize; i++) { + if (parcIterator_HasNext(iterator)) { + parcIterator_Next(iterator); + } + } + parcIterator_Remove(iterator); + + parcIterator_Release(&iterator); + + assertTrue(parcLinkedList_Size(x) == listSize - 1, "Expected the list to be %zd, actual %zd", listSize - 1, parcLinkedList_Size(x)); + + + iterator = parcLinkedList_CreateIterator(x); + size_t expected = 0; + while (parcIterator_HasNext(iterator)) { + size_t actual = parcBuffer_GetUint64(parcIterator_Next(iterator)); + assertTrue(expected == actual, "Expected %zd, actual %zd", expected, actual); + expected++; + } + parcIterator_Release(&iterator); + + assertTrue(parcLinkedList_IsValid(x), "PARCLinkedList is invalid."); + parcLinkedList_Release(&x); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_SetEquals_True) +{ + PARCBuffer *object1 = parcBuffer_WrapCString("1"); + PARCBuffer *object2 = parcBuffer_WrapCString("2"); + PARCBuffer *object3 = parcBuffer_WrapCString("3"); + + PARCLinkedList *x = parcLinkedList_Create(); + parcLinkedList_Append(x, object1); + parcLinkedList_Append(x, object2); + PARCLinkedList *y = parcLinkedList_Create(); + parcLinkedList_Append(y, object2); + parcLinkedList_Append(y, object1); + + PARCLinkedList *u1 = parcLinkedList_Create(); + parcLinkedList_Append(u1, object2); + + PARCLinkedList *u2 = parcLinkedList_Create(); + parcLinkedList_Append(u2, object2); + parcLinkedList_Append(u2, object3); + + assertTrue(parcLinkedList_SetEquals(x, y), "Expected to lists with the same elements to be equal regarless of order."); + + parcLinkedList_Release(&x); + parcLinkedList_Release(&y); + parcLinkedList_Release(&u1); + parcLinkedList_Release(&u2); + + parcBuffer_Release(&object1); + parcBuffer_Release(&object2); + parcBuffer_Release(&object3); +} + +LONGBOW_TEST_CASE(Global, parcLinkedList_SetEquals_False) +{ + PARCBuffer *object1 = parcBuffer_WrapCString("1"); + PARCBuffer *object2 = parcBuffer_WrapCString("2"); + PARCBuffer *object3 = parcBuffer_WrapCString("3"); + + PARCLinkedList *x = parcLinkedList_Create(); + parcLinkedList_Append(x, object1); + parcLinkedList_Append(x, object2); + + PARCLinkedList *u1 = parcLinkedList_Create(); + parcLinkedList_Append(u1, object2); + + PARCLinkedList *u2 = parcLinkedList_Create(); + parcLinkedList_Append(u2, object2); + parcLinkedList_Append(u2, object3); + + assertFalse(parcLinkedList_SetEquals(x, u1), "Expected to lists without the same elements to be equal regarless of order."); + + parcLinkedList_Release(&x); + parcLinkedList_Release(&u1); + parcLinkedList_Release(&u2); + + parcBuffer_Release(&object1); + parcBuffer_Release(&object2); + parcBuffer_Release(&object3); +} + +LONGBOW_TEST_FIXTURE(Local) +{ + LONGBOW_RUN_TEST_CASE(Local, _parcLinkedListNode_Create); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + bool leaked = parcMemoryTesting_ExpectedOutstanding(0, "%s leaks memory \n", longBowTestCase_GetName(testCase)) != true; + if (leaked) { + parcSafeMemory_ReportAllocation(STDOUT_FILENO); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Local, _parcLinkedListNode_Create) +{ + PARCBuffer *object = parcBuffer_Allocate(10); + struct parc_linkedlist_node *previous = NULL; + struct parc_linkedlist_node *next = NULL; + + struct parc_linkedlist_node *actual = _parcLinkedListNode_Create(object, previous, next); + parcBuffer_Release(&object); + _parcLinkedListNode_Destroy(NULL, &actual); +} + +LONGBOW_TEST_FIXTURE_OPTIONS(Performance, .enabled = false) +{ + LONGBOW_RUN_TEST_CASE(Performance, parcLinkedList_Append); + LONGBOW_RUN_TEST_CASE(Performance, parcLinkedList_N2); + LONGBOW_RUN_TEST_CASE(Performance, parcLinkedList_CreateIterator); +} + +LONGBOW_TEST_FIXTURE_SETUP(Performance) +{ + parcMemory_SetInterface(&PARCStdlibMemoryAsPARCMemory); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Performance) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Performance, parcLinkedList_Append) +{ + PARCLinkedList *x = parcLinkedList_Create(); + PARCBuffer *object1 = parcBuffer_WrapCString("1"); + + for (size_t i = 0; i < 100000; i++) { + parcLinkedList_Append(x, object1); + } + + parcBuffer_Release(&object1); + parcLinkedList_Release(&x); +} + +LONGBOW_TEST_CASE(Performance, parcLinkedList_N2) +{ + PARCLinkedList *x = parcLinkedList_Create(); + PARCBuffer *object1 = parcBuffer_WrapCString("1"); + + for (size_t i = 0; i < 100000; i++) { + parcLinkedList_Append(x, object1); + } + + for (size_t expected = 0; expected < parcLinkedList_Size(x); expected++) { + PARCBuffer *actual = (PARCBuffer *) parcLinkedList_GetAtIndex(x, expected); + assertTrue(parcBuffer_Equals(object1, actual), "Mismatched value in the list."); + } + + parcBuffer_Release(&object1); + + parcLinkedList_Release(&x); +} + +LONGBOW_TEST_CASE(Performance, parcLinkedList_CreateIterator) +{ + PARCLinkedList *x = parcLinkedList_Create(); + + uint32_t expectedCount = 100000; + for (uint32_t i = 0; i < expectedCount; i++) { + PARCBuffer *object = parcBuffer_Allocate(sizeof(int)); + parcBuffer_PutUint32(object, i); + parcBuffer_Flip(object); + parcLinkedList_Append(x, object); + parcBuffer_Release(&object); + } + + PARCIterator *iterator = parcLinkedList_CreateIterator(x); + uint32_t expected = 0; + while (parcIterator_HasNext(iterator)) { + PARCBuffer *buffer = (PARCBuffer *) parcIterator_Next(iterator); + uint32_t actual = parcBuffer_GetUint32(buffer); + assertTrue(expected == actual, "Expected %d, actual %d", expected, actual); + expected++; + } + parcIterator_Release(&iterator); + + parcLinkedList_Release(&x); +} + + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(PARCLinkedList); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_List.c b/libparc/parc/algol/test/test_parc_List.c new file mode 100644 index 00000000..59a7d136 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_List.c @@ -0,0 +1,872 @@ +/* + * 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 "../parc_List.c" +#include <LongBow/unit-test.h> + +#include <stdio.h> +#include <string.h> + +#include <parc/testing/parc_MemoryTesting.h> +#include <parc/algol/parc_SafeMemory.h> + +#include <parc/algol/parc_ArrayList.h> +#include <parc/algol/parc_LinkedList.h> +#include <parc/testing/parc_ObjectTesting.h> + +LONGBOW_TEST_RUNNER(PARCList) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(PARCList); + LONGBOW_RUN_TEST_FIXTURE(Local); +// LONGBOW_RUN_TEST_FIXTURE(Errors); +} + +LONGBOW_TEST_RUNNER_SETUP(PARCList) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_RUNNER_TEARDOWN(PARCList) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestRunner_GetName(testRunner), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, PARCList_Add); + LONGBOW_RUN_TEST_CASE(Global, PARCList_AddAll); + LONGBOW_RUN_TEST_CASE(Global, PARCList_Copy); + LONGBOW_RUN_TEST_CASE(Global, parcList_Release); + + LONGBOW_RUN_TEST_CASE(Global, PARCList_Equals_Contract); + LONGBOW_RUN_TEST_CASE(Global, PARCList_Equals_Contract_Deep); + + LONGBOW_RUN_TEST_CASE(Global, PARCList_FromInitialCapacity); + LONGBOW_RUN_TEST_CASE(Global, PARCList_Get); + LONGBOW_RUN_TEST_CASE(Global, PARCList_New); + LONGBOW_RUN_TEST_CASE(Global, PARCList_Length); + LONGBOW_RUN_TEST_CASE(Global, PARCList_Remove_AtIndex_First); + LONGBOW_RUN_TEST_CASE(Global, PARCList_Remove_AtIndex); + LONGBOW_RUN_TEST_CASE(Global, PARCList_Remove_AtIndex_Last); + LONGBOW_RUN_TEST_CASE(Global, PARCList_RemoveAndDestroy_AtIndex_First); + LONGBOW_RUN_TEST_CASE(Global, PARCList_RemoveAndDestroy_AtIndex); + LONGBOW_RUN_TEST_CASE(Global, PARCList_RemoveAndDestroy_AtIndex_Last); + LONGBOW_RUN_TEST_CASE(Global, PARCList_InsertAtIndex); + LONGBOW_RUN_TEST_CASE(Global, PARCList_InsertAtIndex_Empty); + LONGBOW_RUN_TEST_CASE(Global, PARCList_InsertAtIndex_First); + LONGBOW_RUN_TEST_CASE(Global, PARCList_InsertAtIndex_Last); + LONGBOW_RUN_TEST_CASE(Global, PARCList_IsEmpty); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, PARCList_Add) +{ + PARCList *list = parcList(parcArrayList_Create(parcArrayList_StdlibFreeFunction), PARCArrayListAsPARCList); + + parcList_Add(list, 0); + size_t actual = parcList_Size(list); + assertTrue(1 == actual, "Expected=%d, actual=%zu", 1, actual); + + parcList_Release(&list); +} + +LONGBOW_TEST_CASE(Global, PARCList_AddAll) +{ + PARCList *list = parcList(parcArrayList_Create(parcArrayList_StdlibFreeFunction), PARCArrayListAsPARCList); + + void *elements[] = { + strdup("a"), + strdup("b"), + strdup("c"), + }; + + parcList_AddAll(list, 3, elements); + size_t actual = parcList_Size(list); + + assertTrue(3 == actual, "Expected=%d, actual=%zu", 3, actual); + + parcList_Release(&list); +} + +LONGBOW_TEST_CASE(Global, PARCList_Copy) +{ + char *a = strdup("apple"); + char *b = strdup("bananna"); + char *c = strdup("cherry"); + + PARCList *list = parcList(parcArrayList_Create(parcArrayList_StdlibFreeFunction), PARCArrayListAsPARCList); + + parcList_Add(list, a); + parcList_Add(list, b); + parcList_Add(list, c); + + parcList_Release(&list); +} + +LONGBOW_TEST_CASE(Global, parcList_Release) +{ + PARCList *list = parcList(parcArrayList_Create(parcArrayList_StdlibFreeFunction), PARCArrayListAsPARCList); + + parcList_Release(&list); + assertNull(list, "Expected null."); +} + +LONGBOW_TEST_CASE(Global, PARCList_Equals_Empty) +{ + PARCArrayList *a = parcArrayList_Create(parcArrayList_StdlibFreeFunction); + PARCArrayList *b = parcArrayList_Create(parcArrayList_StdlibFreeFunction); + assertTrue(parcArrayList_Equals(a, b), "Equal values were expected to be equal"); + + parcArrayList_Destroy(&a); + parcArrayList_Destroy(&b); +} + +LONGBOW_TEST_CASE(Global, PARCList_Equals_Same) +{ + PARCArrayList *a = parcArrayList_Create(parcArrayList_StdlibFreeFunction); + assertTrue(parcArrayList_Equals(a, a), "Expected the same array list to be equal to itself."); + + parcArrayList_Destroy(&a); +} + +LONGBOW_TEST_CASE(Global, PARCList_Equals_Contract) +{ + char a[] = "apple"; + char b[] = "bananna"; + char c[] = "cherry"; + char d[] = "potato"; + + PARCArrayList *x = parcArrayList_Create(NULL); + parcArrayList_Add(x, a); + parcArrayList_Add(x, b); + parcArrayList_Add(x, c); + + PARCArrayList *y = parcArrayList_Create(NULL); + parcArrayList_Add(y, a); + parcArrayList_Add(y, b); + parcArrayList_Add(y, c); + + PARCArrayList *z = parcArrayList_Create(NULL); + parcArrayList_Add(z, a); + parcArrayList_Add(z, b); + parcArrayList_Add(z, c); + + PARCArrayList *u1 = parcArrayList_Create(NULL); + parcArrayList_Add(u1, a); + parcArrayList_Add(u1, b); + + PARCArrayList *u2 = parcArrayList_Create(NULL); + parcArrayList_Add(u1, a); + parcArrayList_Add(u2, b); + parcArrayList_Add(u2, c); + parcArrayList_Add(u2, c); + + PARCArrayList *u3 = parcArrayList_Create(NULL); + parcArrayList_Add(u3, a); + parcArrayList_Add(u3, b); + parcArrayList_Add(u3, d); + + parcObjectTesting_AssertEqualsFunction(parcArrayList_Equals, x, y, z, u1, u2, u3); + + parcArrayList_Destroy(&x); + parcArrayList_Destroy(&y); + parcArrayList_Destroy(&z); + parcArrayList_Destroy(&u1); + parcArrayList_Destroy(&u2); + parcArrayList_Destroy(&u3); +} + +static bool +stringEquals(void *x, void *y) +{ + return strcmp((char *) x, (char *) y) == 0; +} + +LONGBOW_TEST_CASE(Global, PARCList_Equals_Contract_Deep) +{ + char a[] = "apple"; + char b[] = "bananna"; + char c[] = "cherry"; + char d[] = "potato"; + + PARCArrayList *x = parcArrayList_Create_Capacity(stringEquals, NULL, 0); + parcArrayList_Add(x, a); + parcArrayList_Add(x, b); + parcArrayList_Add(x, c); + + PARCArrayList *y = parcArrayList_Create_Capacity(stringEquals, NULL, 0); + parcArrayList_Add(y, a); + parcArrayList_Add(y, b); + parcArrayList_Add(y, c); + + PARCArrayList *z = parcArrayList_Create_Capacity(stringEquals, NULL, 0); + parcArrayList_Add(z, a); + parcArrayList_Add(z, b); + parcArrayList_Add(z, c); + + PARCArrayList *u1 = parcArrayList_Create_Capacity(stringEquals, NULL, 0); + parcArrayList_Add(u1, a); + parcArrayList_Add(u1, b); + + PARCArrayList *u2 = parcArrayList_Create_Capacity(stringEquals, NULL, 0); + parcArrayList_Add(u2, a); + parcArrayList_Add(u2, b); + parcArrayList_Add(u2, c); + parcArrayList_Add(u2, c); + + PARCArrayList *u3 = parcArrayList_Create_Capacity(stringEquals, NULL, 0); + parcArrayList_Add(u3, a); + parcArrayList_Add(u3, b); + parcArrayList_Add(u3, d); + + parcObjectTesting_AssertEqualsFunction(parcArrayList_Equals, x, y, z, u1, u2, u3); + + parcArrayList_Destroy(&x); + parcArrayList_Destroy(&y); + parcArrayList_Destroy(&z); + parcArrayList_Destroy(&u1); + parcArrayList_Destroy(&u2); + parcArrayList_Destroy(&u3); +} + +LONGBOW_TEST_CASE(Global, PARCList_FromInitialCapacity) +{ + PARCArrayList *array = parcArrayList_Create_Capacity(NULL, parcArrayList_StdlibFreeFunction, 10); + size_t actual = parcArrayList_Size(array); + + assertTrue(0 == actual, "Expected=%d, actual=%zu", 0, actual); + + parcArrayList_Destroy(&array); +} + +LONGBOW_TEST_CASE(Global, PARCList_Get) +{ + PARCArrayList *array = parcArrayList_Create(parcArrayList_StdlibFreeFunction); + + char *expected = strdup("Hello World"); + parcArrayList_Add(array, expected); + + char *actual = parcArrayList_Get(array, 0); + + assertTrue(expected == actual, "Expected=%p, actual=%p", (void *) expected, (void *) actual); + + parcArrayList_Destroy(&array); +} + +LONGBOW_TEST_CASE(Global, PARCList_New) +{ + PARCArrayList *array = parcArrayList_Create(parcArrayList_StdlibFreeFunction); + size_t size = parcArrayList_Size(array); + assertTrue(0 == size, "Expected %d actual=%zd", 0, size); + + parcArrayList_Destroy(&array); +} + +LONGBOW_TEST_CASE(Global, PARCList_Length) +{ + PARCArrayList *array = parcArrayList_Create(NULL); + parcArrayList_Add(array, 0); + + size_t size = parcArrayList_Size(array); + assertTrue(1 == size, "Expected %d actual=%zd", 1, size); + parcArrayList_Destroy(&array); +} + +LONGBOW_TEST_CASE(Global, PARCList_IsEmpty) +{ + PARCArrayList *array = parcArrayList_Create(NULL); + assertTrue(parcArrayList_IsEmpty(array), "Expected a new array to be empty."); + + parcArrayList_Add(array, 0); + assertFalse(parcArrayList_IsEmpty(array), "Expected an array with more than zero elements to be empty."); + + parcArrayList_Destroy(&array); +} + +LONGBOW_TEST_CASE(Global, PARCList_InsertAtIndex) +{ + PARCArrayList *array = parcArrayList_Create(NULL); + + parcArrayList_Add(array, (void *) 1); + parcArrayList_Add(array, (void *) 2); + size_t actual = parcArrayList_Size(array); + + assertTrue(2 == actual, "Expected=%d, actual=%zu", 2, actual); + + parcArrayList_InsertAtIndex(array, 1, (void *) 3); + + actual = parcArrayList_Size(array); + assertTrue(3 == actual, "Expected=%d, actual=%zu", 3, actual); + + void *element0 = parcArrayList_Get(array, 0); + assertTrue(element0 == (void *) 1, "Element 1 moved?"); + + void *element1 = parcArrayList_Get(array, 1); + assertTrue(element1 == (void *) 3, "Element 1 moved?"); + + void *element2 = parcArrayList_Get(array, 2); + assertTrue(element2 == (void *) 2, "Element 1 moved?"); + + parcArrayList_Destroy(&array); +} + +LONGBOW_TEST_CASE(Global, PARCList_InsertAtIndex_Empty) +{ + PARCArrayList *array = parcArrayList_Create(NULL); + + parcArrayList_InsertAtIndex(array, 0, (void *) 3); + + size_t actual = parcArrayList_Size(array); + + assertTrue(1 == actual, "Expected=%d, actual=%zu", 1, actual); + + parcArrayList_Destroy(&array); +} + +LONGBOW_TEST_CASE(Global, PARCList_InsertAtIndex_First) +{ + PARCArrayList *array = parcArrayList_Create(NULL); + + parcArrayList_Add(array, (void *) 1); + parcArrayList_InsertAtIndex(array, 0, (void *) 2); + size_t actual = parcArrayList_Size(array); + + assertTrue(2 == actual, "Expected=%d, actual=%zu", 2, actual); + + void *element0 = parcArrayList_Get(array, 0); + assertTrue(element0 == (void *) 2, "Element 1 moved?"); + + void *element1 = parcArrayList_Get(array, 1); + assertTrue(element1 == (void *) 1, "Element 1 moved?"); + + parcArrayList_Destroy(&array); +} + +LONGBOW_TEST_CASE(Global, PARCList_InsertAtIndex_Last) +{ + PARCArrayList *array = parcArrayList_Create(NULL); + + parcArrayList_Add(array, (void *) 1); + parcArrayList_Add(array, (void *) 2); + size_t actual = parcArrayList_Size(array); + + assertTrue(2 == actual, "Expected=%d, actual=%zu", 2, actual); + + parcArrayList_InsertAtIndex(array, 2, (void *) 3); + + actual = parcArrayList_Size(array); + assertTrue(3 == actual, "Expected=%d, actual=%zu", 3, actual); + + void *element0 = parcArrayList_Get(array, 0); + assertTrue(element0 == (void *) 1, "Element 1 moved?"); + + void *element1 = parcArrayList_Get(array, 1); + assertTrue(element1 == (void *) 2, "Element 1 moved?"); + + void *element2 = parcArrayList_Get(array, 2); + assertTrue(element2 == (void *) 3, "Element 1 moved?"); + + parcArrayList_Destroy(&array); +} + +LONGBOW_TEST_CASE(Global, PARCList_Remove_AtIndex_First) +{ + char a[] = "apple"; + char b[] = "bananna"; + char c[] = "cherry"; + + PARCArrayList *array = parcArrayList_Create(NULL); + parcArrayList_Add(array, a); + parcArrayList_Add(array, b); + parcArrayList_Add(array, c); + + PARCArrayList *expected = parcArrayList_Create(NULL); + parcArrayList_Add(expected, b); + parcArrayList_Add(expected, c); + + void *removedElement = parcArrayList_RemoveAtIndex(array, 0); + + assertTrue(removedElement == a, "Expected "); + assertTrue(parcArrayList_Equals(expected, array), "Expected "); + + parcArrayList_Destroy(&expected); + parcArrayList_Destroy(&array); +} + +LONGBOW_TEST_CASE(Global, PARCList_Remove_AtIndex) +{ + char a[] = "apple"; + char b[] = "bananna"; + char c[] = "cherry"; + + PARCArrayList *array = parcArrayList_Create(NULL); + parcArrayList_Add(array, a); + parcArrayList_Add(array, b); + parcArrayList_Add(array, c); + + PARCArrayList *expected = parcArrayList_Create(NULL); + parcArrayList_Add(expected, a); + parcArrayList_Add(expected, c); + + void *removedElement = parcArrayList_RemoveAtIndex(array, 1); + + assertTrue(removedElement == b, "Expected "); + assertTrue(parcArrayList_Equals(expected, array), "Expected "); + + parcArrayList_Destroy(&expected); + parcArrayList_Destroy(&array); +} + + +LONGBOW_TEST_CASE(Global, PARCList_Remove_AtIndex_Last) +{ + char a[] = "apple"; + char b[] = "bananna"; + char c[] = "cherry"; + + PARCArrayList *array = parcArrayList_Create(NULL); + parcArrayList_Add(array, a); + parcArrayList_Add(array, b); + parcArrayList_Add(array, c); + + PARCArrayList *expected = parcArrayList_Create(NULL); + parcArrayList_Add(expected, a); + parcArrayList_Add(expected, b); + + void *removedElement = parcArrayList_RemoveAtIndex(array, 2); + + assertTrue(removedElement == c, "Expected "); + assertTrue(parcArrayList_Equals(expected, array), "Expected "); + + parcArrayList_Destroy(&expected); + parcArrayList_Destroy(&array); +} + +LONGBOW_TEST_CASE(Global, PARCList_RemoveAndDestroy_AtIndex_First) +{ + char a[] = "apple"; + char b[] = "bananna"; + char c[] = "cherry"; + + PARCArrayList *array = parcArrayList_Create(NULL); + parcArrayList_Add(array, a); + parcArrayList_Add(array, b); + parcArrayList_Add(array, c); + + PARCArrayList *expected = parcArrayList_Create(NULL); + parcArrayList_Add(expected, b); + parcArrayList_Add(expected, c); + + parcArrayList_RemoveAndDestroyAtIndex(array, 0); + + assertTrue(parcArrayList_Equals(expected, array), "Expected "); + + parcArrayList_Destroy(&expected); + parcArrayList_Destroy(&array); +} + +LONGBOW_TEST_CASE(Global, PARCList_RemoveAndDestroy_AtIndex) +{ + char a[] = "apple"; + char b[] = "bananna"; + char c[] = "cherry"; + + PARCArrayList *array = parcArrayList_Create(NULL); + parcArrayList_Add(array, a); + parcArrayList_Add(array, b); + parcArrayList_Add(array, c); + + PARCArrayList *expected = parcArrayList_Create(NULL); + parcArrayList_Add(expected, a); + parcArrayList_Add(expected, c); + + parcArrayList_RemoveAndDestroyAtIndex(array, 1); + + assertTrue(parcArrayList_Equals(expected, array), "Expected "); + + parcArrayList_Destroy(&expected); + parcArrayList_Destroy(&array); +} + +LONGBOW_TEST_CASE(Global, PARCList_RemoveAndDestroy_AtIndex_Last) +{ + char a[] = "apple"; + char b[] = "bananna"; + char c[] = "cherry"; + + PARCArrayList *array = parcArrayList_Create(NULL); + parcArrayList_Add(array, a); + parcArrayList_Add(array, b); + parcArrayList_Add(array, c); + + PARCArrayList *expected = parcArrayList_Create(NULL); + parcArrayList_Add(expected, a); + parcArrayList_Add(expected, b); + + parcArrayList_RemoveAndDestroyAtIndex(array, 2); + + assertTrue(parcArrayList_Equals(expected, array), "Expected "); + + parcArrayList_Destroy(&expected); + parcArrayList_Destroy(&array); +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Errors) +{ + LONGBOW_RUN_TEST_CASE(Errors, PARCList_InsertAtIndex_OutOfCapacity); +} + +LONGBOW_TEST_FIXTURE_SETUP(Errors) +{ + PARCArrayList *array = parcArrayList_Create(NULL); + + longBowTestCase_SetClipBoardData(testCase, array); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Errors) +{ + PARCArrayList *array = longBowTestCase_GetClipBoardData(testCase); + parcArrayList_Destroy(&array); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("Errors %s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE_EXPECTS(Errors, PARCList_InsertAtIndex_OutOfCapacity, .event = &LongBowAssertEvent) +{ + PARCArrayList *array = longBowTestCase_GetClipBoardData(testCase); + + parcArrayList_Add(array, (void *) 1); + parcArrayList_Add(array, (void *) 2); + + parcArrayList_InsertAtIndex(array, 200, (void *) 3); +} + + +LONGBOW_TEST_FIXTURE(PARCList) +{ + LONGBOW_RUN_TEST_CASE(PARCList, parcList_Add); + LONGBOW_RUN_TEST_CASE(PARCList, parcList_AddCollection); + LONGBOW_RUN_TEST_CASE(PARCList, parcList_AddCollectionAtIndex); + LONGBOW_RUN_TEST_CASE(PARCList, parcList_Contains); + LONGBOW_RUN_TEST_CASE(PARCList, parcList_ContainsCollection); + LONGBOW_RUN_TEST_CASE(PARCList, parcList_Equals); + LONGBOW_RUN_TEST_CASE(PARCList, parcList_IsEmpty); + LONGBOW_RUN_TEST_CASE(PARCList, parcList_GetAtIndex); + LONGBOW_RUN_TEST_CASE(PARCList, parcList_Remove); + LONGBOW_RUN_TEST_CASE(PARCList, parcList_RemoveCollection); + LONGBOW_RUN_TEST_CASE(PARCList, parcList_RetainCollection); + LONGBOW_RUN_TEST_CASE(PARCList, parcList_HashCode); + LONGBOW_RUN_TEST_CASE(PARCList, parcList_IndexOf); + LONGBOW_RUN_TEST_CASE(PARCList, parcList_LastIndexOf); + LONGBOW_RUN_TEST_CASE(PARCList, parcList_Copy); + LONGBOW_RUN_TEST_CASE(PARCList, parcList_Clear); + LONGBOW_RUN_TEST_CASE(PARCList, parcList_Destroy); + LONGBOW_RUN_TEST_CASE(PARCList, parcList_RemoveAtIndex); + LONGBOW_RUN_TEST_CASE(PARCList, parcList_SetAtIndex); + LONGBOW_RUN_TEST_CASE(PARCList, parcList_Size); + LONGBOW_RUN_TEST_CASE(PARCList, parcList_SubList); + LONGBOW_RUN_TEST_CASE(PARCList, parcList_ToArray); +} + +LONGBOW_TEST_FIXTURE_SETUP(PARCList) +{ + longBowTestCase_SetInt(testCase, "initialAllocations", parcMemory_Outstanding()); + longBowTestCase_Set(testCase, "linkedList", parcLinkedList_Create()); + longBowTestCase_Set(testCase, "list", parcLinkedList_AsPARCList(longBowTestCase_Get(testCase, "linkedList"))); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(PARCList) +{ + PARCLinkedList *linkedList = longBowTestCase_Get(testCase, "linkedList"); + parcLinkedList_Release(&linkedList); + PARCList *list = longBowTestCase_Get(testCase, "list"); + parcList_Release(&list); + + int initialAllocations = longBowTestCase_GetInt(testCase, "initalAllocations"); + if (!parcMemoryTesting_ExpectedOutstanding(initialAllocations, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) { + parcSafeMemory_ReportAllocation(STDOUT_FILENO); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +//#include "test_parc_List_modular.c" + +LONGBOW_TEST_CASE(PARCList, parcList_Add) +{ + PARCList *list = longBowTestCase_Get(testCase, "list"); + + PARCBuffer *buffer = parcBuffer_Flip(parcBuffer_PutUint32(parcBuffer_Allocate(sizeof(int)), 1)); + parcList_Add(list, buffer); + parcBuffer_Release(&buffer); + + size_t actual = parcList_Size(list); + assertTrue(1 == actual, "Expected=%d, actual=%zu", 1, actual); +} + +LONGBOW_TEST_CASE(PARCList, parcList_AddCollection) +{ +} + +LONGBOW_TEST_CASE(PARCList, parcList_AddCollectionAtIndex) +{ +} + +LONGBOW_TEST_CASE(PARCList, parcList_Contains) +{ +} + +LONGBOW_TEST_CASE(PARCList, parcList_ContainsCollection) +{ +} + +LONGBOW_TEST_CASE(PARCList, parcList_Equals) +{ + PARCList *list = longBowTestCase_Get(testCase, "list"); + PARCList *copy = parcList_Copy(list); + + assertTrue(parcList_Equals(list, copy), "Expected copy to be equal to the original."); + + parcList_Release(©); +} + +LONGBOW_TEST_CASE(PARCList, parcList_IsEmpty) +{ + PARCList *list = longBowTestCase_Get(testCase, "list"); + assertTrue(parcList_IsEmpty(list), "Expected list to be empty."); +} + +LONGBOW_TEST_CASE(PARCList, parcList_GetAtIndex) +{ + PARCList *list = longBowTestCase_Get(testCase, "list"); + + for (int i = 0; i < 1000; i++) { + PARCBuffer *buffer = parcBuffer_Flip(parcBuffer_PutUint32(parcBuffer_Allocate(sizeof(int)), i)); + parcList_Add(list, buffer); + parcBuffer_Release(&buffer); + } + + uint32_t actual = parcBuffer_GetUint32(parcList_GetAtIndex(list, 0)); + + assertTrue(actual == 0, "Expected %u, actual %u\n", 0, actual); +} + +LONGBOW_TEST_CASE(PARCList, parcList_Remove) +{ + PARCList *list = longBowTestCase_Get(testCase, "list"); + + PARCBuffer *buffer = parcBuffer_Flip(parcBuffer_PutUint32(parcBuffer_Allocate(sizeof(int)), 1)); + parcList_Add(list, buffer); + parcBuffer_Release(&buffer); + + buffer = parcBuffer_Flip(parcBuffer_PutUint32(parcBuffer_Allocate(sizeof(int)), 1)); + + bool actual = parcList_Remove(list, buffer); + assertTrue(actual, "Expected element to have been found and removed."); + + parcBuffer_Release(&buffer); + + buffer = parcBuffer_Flip(parcBuffer_PutUint32(parcBuffer_Allocate(sizeof(int)), 3)); + + actual = parcList_Remove(list, buffer); + assertFalse(actual, "Expected element to have not been found and removed."); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(PARCList, parcList_RemoveCollection) +{ +} + +LONGBOW_TEST_CASE(PARCList, parcList_RetainCollection) +{ +} + +LONGBOW_TEST_CASE(PARCList, parcList_HashCode) +{ + PARCList *list = longBowTestCase_Get(testCase, "list"); + parcList_HashCode(list); +} + +LONGBOW_TEST_CASE(PARCList, parcList_IndexOf) +{ + PARCList *list = longBowTestCase_Get(testCase, "list"); + + for (int i = 0; i < 1000; i++) { + PARCBuffer *buffer = parcBuffer_Flip(parcBuffer_PutUint32(parcBuffer_Allocate(sizeof(int)), i)); + parcList_Add(list, buffer); + parcBuffer_Release(&buffer); + } + + uint32_t expected = 10; + PARCBuffer *buffer = parcBuffer_Flip(parcBuffer_PutUint32(parcBuffer_Allocate(sizeof(int)), 10)); + size_t actual = parcList_IndexOf(list, buffer); + + parcBuffer_Release(&buffer); + + assertTrue(expected == actual, "Expected %u, actual %zu", expected, actual); +} + +LONGBOW_TEST_CASE(PARCList, parcList_LastIndexOf) +{ + PARCList *list = longBowTestCase_Get(testCase, "list"); + + for (int i = 0; i < 1000; i++) { + PARCBuffer *buffer = parcBuffer_Flip(parcBuffer_PutUint32(parcBuffer_Allocate(sizeof(int)), 1)); + parcList_Add(list, buffer); + parcBuffer_Release(&buffer); + } + + uint32_t expected = 999; + PARCBuffer *buffer = parcBuffer_Flip(parcBuffer_PutUint32(parcBuffer_Allocate(sizeof(int)), 1)); + size_t actual = parcList_LastIndexOf(list, buffer); + + parcBuffer_Release(&buffer); + + assertTrue(expected == actual, "Expected %u, actual %zu", expected, actual); +} + +LONGBOW_TEST_CASE(PARCList, parcList_Copy) +{ + PARCList *list = longBowTestCase_Get(testCase, "list"); + PARCList *copy = parcList_Copy(list); + + assertTrue(parcList_Equals(list, copy), "Expected copy to be equal to the original."); + + parcList_Release(©); +} + +LONGBOW_TEST_CASE(PARCList, parcList_Clear) +{ + PARCList *list = longBowTestCase_Get(testCase, "list"); + parcList_Clear(list); + + assertTrue(parcList_IsEmpty(list), "Expected list to be empty."); +} + +LONGBOW_TEST_CASE(PARCList, parcList_Destroy) +{ + PARCList *list = longBowTestCase_Get(testCase, "list"); + PARCList *copy = parcList_Copy(list); + + parcList_Release(©); +} + +LONGBOW_TEST_CASE(PARCList, parcList_RemoveAtIndex) +{ + PARCList *list = longBowTestCase_Get(testCase, "list"); + + for (int i = 0; i < 1000; i++) { + PARCBuffer *buffer = parcBuffer_Flip(parcBuffer_PutUint32(parcBuffer_Allocate(sizeof(int)), i)); + parcList_Add(list, buffer); + parcBuffer_Release(&buffer); + } + + PARCBuffer *buffer = parcList_RemoveAtIndex(list, 0); + uint32_t actual = parcBuffer_GetUint32(buffer); + assertTrue(actual == 0, "Expected buffer 0."); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(PARCList, parcList_SetAtIndex) +{ + PARCList *list = longBowTestCase_Get(testCase, "list"); + + for (int i = 0; i < 1000; i++) { + PARCBuffer *buffer = parcBuffer_Flip(parcBuffer_PutUint32(parcBuffer_Allocate(sizeof(int)), i)); + parcList_Add(list, buffer); + parcBuffer_Release(&buffer); + } + + PARCBuffer *buffer = parcBuffer_WrapCString("1"); + + PARCBuffer *oldValue = parcList_SetAtIndex(list, 50, buffer); + + PARCBuffer *actual = parcList_GetAtIndex(list, 50); + assertTrue(parcBuffer_Equals(buffer, actual), "parcList_SetAtIndex set the wrong location."); + parcBuffer_Release(&buffer); + parcBuffer_Release(&oldValue); +} + +LONGBOW_TEST_CASE(PARCList, parcList_Size) +{ + PARCArrayList *array = parcArrayList_Create(NULL); + parcArrayList_Add(array, 0); + + size_t size = parcArrayList_Size(array); + assertTrue(1 == size, "Expected %d actual=%zd", 1, size); + parcArrayList_Destroy(&array); +} + +LONGBOW_TEST_CASE(PARCList, parcList_SubList) +{ +} + +LONGBOW_TEST_CASE(PARCList, parcList_ToArray) +{ +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(PARCList); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_Memory.c b/libparc/parc/algol/test/test_parc_Memory.c new file mode 100755 index 00000000..5fb18ce3 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_Memory.c @@ -0,0 +1,220 @@ +/* + * 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 <stdio.h> + +#include <LongBow/unit-test.h> + +#include <parc/algol/parc_SafeMemory.h> + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../parc_Memory.c" + +LONGBOW_TEST_RUNNER(parc_Memory) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Static); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_Memory) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_Memory) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcMemory_RoundUpToMultiple); + LONGBOW_RUN_TEST_CASE(Global, parcMemory_RoundUpToCacheLine); + LONGBOW_RUN_TEST_CASE(Global, parcMemory_Allocate); + LONGBOW_RUN_TEST_CASE(Global, parcMemory_AllocateAndClear); + LONGBOW_RUN_TEST_CASE(Global, parcMemory_MemAlign); + LONGBOW_RUN_TEST_CASE(Global, parcMemory_Reallocate); + LONGBOW_RUN_TEST_CASE(Global, parcMemory_StringDuplicate); + LONGBOW_RUN_TEST_CASE(Global, parcMemory_Outstanding); + LONGBOW_RUN_TEST_CASE(Global, parcMemory_SetInterface); + LONGBOW_RUN_TEST_CASE(Global, parcMemory_Format); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcMemory_RoundUpToMultiple) +{ + size_t actual = parcMemory_RoundUpToMultiple(14, 12); + assertTrue((actual % 12) == 0, "Expected %zd to be a multiple of %d", actual, 12); + assertTrue(24 == actual, "Expected 24, actual %zd", actual); + + actual = parcMemory_RoundUpToMultiple(14, 20); + assertTrue((actual % 20) == 0, "Expected %zd to be a multiple of %d", actual, 20); + assertTrue(20 == actual, "Expected 20, actual %zd", actual); + + actual = parcMemory_RoundUpToMultiple(20, 20); + assertTrue((actual % 20) == 0, "Expected %zd to be a multiple of %d", actual, 20); + assertTrue(20 == actual, "Expected 20, actual %zd", actual); + + actual = parcMemory_RoundUpToMultiple(0, 20); + assertTrue((actual % 20) == 0, "Expected %zd to be a multiple of %d", actual, 20); + assertTrue(20 == actual, "Expected 20, actual %zd", actual); + + actual = parcMemory_RoundUpToMultiple(8, 0); + assertTrue(actual == 8, "Expected %d, actual %zd", 8, actual); +} + +LONGBOW_TEST_CASE(Global, parcMemory_RoundUpToCacheLine) +{ + size_t actual = parcMemory_RoundUpToCacheLine(LEVEL1_DCACHE_LINESIZE - 1); + assertTrue((actual % LEVEL1_DCACHE_LINESIZE) == 0, + "Expected %zd to be a multiple of %d", actual, LEVEL1_DCACHE_LINESIZE); +} + +LONGBOW_TEST_CASE(Global, parcMemory_Allocate) +{ + void *pointer; + pointer = parcMemory_Allocate(sizeof(int)); + assertNotNull(pointer, "Expected pointer to not be NULL"); + + parcMemory_Deallocate(&pointer); + assertNull(pointer, "Expected pointer to not be NULL"); +} + +LONGBOW_TEST_CASE(Global, parcMemory_MemAlign) +{ + void *pointer; + int actual = parcMemory_MemAlign(&pointer, sizeof(void *), sizeof(int)); + assertTrue(actual == 0, "Expected successful return value."); + assertNotNull(pointer, "Expected pointer to not be NULL"); + + parcMemory_Deallocate(&pointer); + assertNull(pointer, "Expected pointer to not be NULL"); +} + +LONGBOW_TEST_CASE(Global, parcMemory_Reallocate) +{ + void *pointer; + int actual = parcMemory_MemAlign(&pointer, sizeof(void *), sizeof(int)); + assertTrue(actual == 0, "Expected successful return value."); + + pointer = parcMemory_Reallocate(pointer, sizeof(int) * 2); + assertNotNull(pointer, "Expected pointer to not be NULL"); + + parcMemory_Deallocate(&pointer); + assertNull(pointer, "Expected pointer to not be NULL"); +} + +LONGBOW_TEST_CASE(Global, parcMemory_AllocateAndClear) +{ + void *pointer; + pointer = parcMemory_AllocateAndClear(sizeof(int)); + assertNotNull(pointer, "Expected pointer to not be NULL"); + + for (int i = 0; i < sizeof(int); i++) { + assertTrue(((char *) pointer)[i] == 0, "Expected cell to be zero."); + } + + parcMemory_Deallocate(&pointer); + assertNull(pointer, "Expected pointer to not be NULL"); +} + +LONGBOW_TEST_CASE(Global, parcMemory_StringDuplicate) +{ + char *expected = "Hello"; + + char *actual = parcMemory_StringDuplicate(expected, strlen(expected)); + + assertTrue(strcmp(expected, actual) == 0, "Expected %s, actual %s", expected, actual); + + parcMemory_Deallocate((void **) &actual); +} + +LONGBOW_TEST_CASE(Global, parcMemory_Outstanding) +{ + void *pointer; + pointer = parcMemory_Allocate(sizeof(int)); + + size_t expected = 1; + size_t actual = parcMemory_Outstanding(); + assertTrue(expected == actual, "Expected %zd, actual %zd", expected, actual); + + parcMemory_Deallocate(&pointer); + + expected = 0; + actual = parcMemory_Outstanding(); + assertTrue(expected == actual, "Expected %zd, actual %zd", expected, actual); +} + +LONGBOW_TEST_CASE(Global, parcMemory_SetInterface) +{ + const PARCMemoryInterface *old = parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + + parcMemory_SetInterface(old); +} + +LONGBOW_TEST_CASE(Global, parcMemory_Format) +{ + char *expected = "Hello World"; + char *actual = parcMemory_Format("Hello %s", "World"); + + assertTrue(strcmp(expected, actual) == 0, + "Expected '%s', actual '%s'", expected, actual); + + parcMemory_Deallocate(&actual); +} + +LONGBOW_TEST_FIXTURE(Static) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Static) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Static) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_Memory); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_Network.c b/libparc/parc/algol/test/test_parc_Network.c new file mode 100755 index 00000000..864fe25d --- /dev/null +++ b/libparc/parc/algol/test/test_parc_Network.c @@ -0,0 +1,668 @@ +/* + * 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 the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../parc_Network.c" + +#include <LongBow/unit-test.h> + +#include <stdio.h> + +#include <parc/algol/parc_SafeMemory.h> +#include <parc/testing/parc_ObjectTesting.h> + +#include <sys/un.h> + +LONGBOW_TEST_RUNNER(parc_Networking) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Local); + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_Networking) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_Networking) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcNetwork_SockInet4AddressAny); + LONGBOW_RUN_TEST_CASE(Global, parcNetwork_SockInet4Address_BuildString); + LONGBOW_RUN_TEST_CASE(Global, parcNetwork_SockInet6Address_BuildString); + LONGBOW_RUN_TEST_CASE(Global, parcNetwork_LinkAddress_BuildString_dashes); + LONGBOW_RUN_TEST_CASE(Global, parcNetwork_LinkAddress_BuildString_colons); + LONGBOW_RUN_TEST_CASE(Global, parcNetwork_Inet4Equals); + + LONGBOW_RUN_TEST_CASE(Global, parcNetwork_LinkAddress_Parse_dashes); + LONGBOW_RUN_TEST_CASE(Global, parcNetwork_LinkAddress_Parse_colons); + LONGBOW_RUN_TEST_CASE(Global, parcNetwork_LinkAddress_Parse_dots); + LONGBOW_RUN_TEST_CASE(Global, parcNetwork_ParseLinkAddress_BadScheme); + LONGBOW_RUN_TEST_CASE(Global, parcNetwork_LinkAddress_Parse_BadLink); + LONGBOW_RUN_TEST_CASE(Global, parcNetwork_LinkAddress_Parse_BadMixOfDashesAndDots); + LONGBOW_RUN_TEST_CASE(Global, parcNetwork_LinkAddress_Parse_BadMixOfDotsAndDashes); + LONGBOW_RUN_TEST_CASE(Global, parcNetwork_LinkAddress_Parse_BadSpecification); + LONGBOW_RUN_TEST_CASE(Global, parcNetwork_ParseInet4Address); + + LONGBOW_RUN_TEST_CASE(Global, parseMAC48Address); + LONGBOW_RUN_TEST_CASE(Global, parcNetwork_ParseMAC48Address_Colons); + LONGBOW_RUN_TEST_CASE(Global, parcNetwork_ParseMAC48Address_Colons_TooShort); + LONGBOW_RUN_TEST_CASE(Global, parcNetwork_ParseMAC48Address_Colons_Garbage); + LONGBOW_RUN_TEST_CASE(Global, parcNetwork_ParseMAC48Address); + LONGBOW_RUN_TEST_CASE(Global, parcNetwork_ParseMAC48Address_TooShort); + + LONGBOW_RUN_TEST_CASE(Global, parcNetwork_SockAddress_ipv4); + LONGBOW_RUN_TEST_CASE(Global, parcNetwork_SockAddress_ipv6); + LONGBOW_RUN_TEST_CASE(Global, parcNetwork_SockAddress_hostname); + + LONGBOW_RUN_TEST_CASE(Global, parcNetwork_IsSocketLocal_PF_LOCAL); + LONGBOW_RUN_TEST_CASE(Global, parcNetwork_IsSocketLocal_PF_INET4); + LONGBOW_RUN_TEST_CASE(Global, parcNetwork_IsSocketLocal_PF_INET6); + LONGBOW_RUN_TEST_CASE(Global, parcNetwork_IsSocketLocal_PF_IPX); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcNetwork_SockInet4AddressAny) +{ + struct sockaddr_in *test_sock = parcNetwork_SockInet4AddressAny(); + + assertNotNull(test_sock, "Expected a not null pointer\n"); + assertTrue(test_sock->sin_family == AF_INET, "Expecting sin_family to be AF_INET\n"); + assertTrue(test_sock->sin_addr.s_addr == INADDR_ANY, "Expecting sin_addr.s_addr to be set to INADDR_ANY\n"); +#if defined(SIN6_LEN) + assertTrue(test_sock->sin_len == sizeof(struct sockaddr_in), "Expecting sockaddr.sin_len to be %zu not %hhu\n", + sizeof(struct sockaddr_in), test_sock->sin_len); +#endif + + parcMemory_Deallocate((void **) &test_sock); +} + +LONGBOW_TEST_CASE(Global, parcNetwork_SockInet4Address_BuildString) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + + struct sockaddr_in *address = parcNetwork_SockInet4Address("127.0.0.1", 1234); +#if defined(SIN6_LEN) + assertTrue(address->sin_len == sizeof(struct sockaddr_in), "Expecting sockaddr.sin_len to be %zu not %hhu\n", + sizeof(struct sockaddr_in), address->sin_len); +#endif + parcNetwork_SockInet4Address_BuildString(address, composer); + + char *expected = "inet4://127.0.0.1:1234"; + + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + char *actual = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + + assertTrue(strcmp(expected, actual) == 0, "Expected '%s', actual '%s'", expected, actual); + + parcMemory_Deallocate((void **) &actual); + parcMemory_Deallocate((void **) &address); + parcBufferComposer_Release(&composer); +} + +LONGBOW_TEST_CASE(Global, parcNetwork_SockInet6Address_BuildString) +{ + struct sockaddr_in6 *address = parcNetwork_SockInet6Address("2001:720:1500:1::a100", 1234, 0, 1); +#if defined(SIN6_LEN) + assertTrue(address->sin6_len == sizeof(struct sockaddr_in6), "Expecting sockaddr.sin6_len to be %zu not %hhu\n", + sizeof(struct sockaddr_in6), address->sin6_len); +#endif + + PARCBufferComposer *composer = parcBufferComposer_Create(); + parcNetwork_SockInet6Address_BuildString(address, composer); + + char *expected = "inet6://[2001:720:1500:1::a100%1]:1234"; + + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + char *actual = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + + assertTrue(strcmp(expected, actual) == 0, "Expected '%s', actual '%s'", expected, actual); + + parcMemory_Deallocate((void **) &actual); + parcMemory_Deallocate((void **) &address); + parcBufferComposer_Release(&composer); +} + +LONGBOW_TEST_CASE(Global, parcNetwork_LinkAddress_BuildString_dashes) +{ + char *expected = "link://01-23-45-67-89-ab"; + + PARCBuffer *address = parcNetwork_ParseLinkAddress(expected); + + PARCBufferComposer *composer = parcBufferComposer_Create(); + + parcNetwork_LinkAddress_BuildString((unsigned char *) parcBuffer_Overlay(address, 0), parcBuffer_Remaining(address), composer); + + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + char *actual = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + + assertTrue(strcmp(expected, actual) == 0, + "Expected '%s', actual '%s'", expected, actual); + + parcMemory_Deallocate((void **) &actual); + parcBufferComposer_Release(&composer); + parcBuffer_Release(&address); +} + +LONGBOW_TEST_CASE(Global, parcNetwork_LinkAddress_BuildString_colons) +{ + char *expected = "link://01-23-45-67-89-ab"; + + PARCBuffer *address = parcNetwork_ParseLinkAddress("link://01:23:45:67:89:ab"); + + PARCBufferComposer *composer = parcBufferComposer_Create(); + parcNetwork_LinkAddress_BuildString((unsigned char *) parcBuffer_Overlay(address, 0), parcBuffer_Remaining(address), composer); + + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + char *actual = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + + assertTrue(strcmp(expected, actual) == 0, "Expected '%s', actual '%s'", expected, actual); + + parcMemory_Deallocate((void **) &actual); + parcBufferComposer_Release(&composer); + parcBuffer_Release(&address); +} + +LONGBOW_TEST_CASE(Global, parcNetwork_LinkAddress_Parse_dashes) +{ + char *expected = "link://01-23-45-67-89-ab"; + PARCBuffer *address = parcNetwork_ParseLinkAddress(expected); + parcBuffer_Flip(address); + + PARCBuffer *e = parcBuffer_Wrap((uint8_t []) { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab }, 6, 0, 6); + + parcBuffer_SetPosition(address, 0); + parcBuffer_SetLimit(address, 6); + + parcBuffer_SetPosition(e, 0); + parcBuffer_SetLimit(e, 6); + + assertTrue(parcBuffer_Equals(address, e), + "Expected result failed."); + + parcBuffer_Release(&e); + parcBuffer_Release(&address); +} + +LONGBOW_TEST_CASE(Global, parcNetwork_LinkAddress_Parse_colons) +{ + char *expected = "link://01:23:45:67:89:ab"; + PARCBuffer *address = parcNetwork_ParseLinkAddress(expected); + + PARCBuffer *e = parcBuffer_Wrap((uint8_t []) { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab }, 6, 0, 6); + + parcBuffer_SetPosition(address, 0); + parcBuffer_SetPosition(e, 0); + parcBuffer_SetLimit(address, 6); + parcBuffer_SetLimit(e, 6); + + assertTrue(parcBuffer_Equals(address, e), + "Expected result failed."); + + parcBuffer_Release(&e); + parcBuffer_Release(&address); +} + +LONGBOW_TEST_CASE(Global, parcNetwork_LinkAddress_Parse_dots) +{ + char *expected = "link://0123.4567.89ab"; + PARCBuffer *address = parcNetwork_ParseLinkAddress(expected); + + PARCBuffer *e = parcBuffer_Wrap((uint8_t []) { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab }, 6, 0, 6); + + parcBuffer_SetPosition(address, 0); + parcBuffer_SetPosition(e, 0); + parcBuffer_SetLimit(address, 6); + parcBuffer_SetLimit(e, 6); + + assertTrue(parcBuffer_Equals(address, e), + "Expected result failed."); + + parcBuffer_Release(&e); + parcBuffer_Release(&address); +} + +LONGBOW_TEST_CASE_EXPECTS(Global, parcNetwork_ParseLinkAddress_BadScheme, .event = &LongBowTrapIllegalValue) +{ + char *expected = "asdf://"; + parcNetwork_ParseLinkAddress(expected); +} + +LONGBOW_TEST_CASE_EXPECTS(Global, parcNetwork_LinkAddress_Parse_BadLink, .event = &LongBowTrapIllegalValue) +{ + char *expected = "link://"; + parcNetwork_ParseLinkAddress(expected); + printf("Hello\n"); +} + +LONGBOW_TEST_CASE_EXPECTS(Global, parcNetwork_LinkAddress_Parse_BadSpecification, .event = &LongBowTrapIllegalValue) +{ + char *expected = "link://a"; + parcNetwork_ParseLinkAddress(expected); +} + +LONGBOW_TEST_CASE_EXPECTS(Global, parcNetwork_LinkAddress_Parse_BadMixOfDashesAndDots, .event = &LongBowTrapIllegalValue) +{ + char *expected = "link://01-23-45.6789ab"; + parcNetwork_ParseLinkAddress(expected); +} + +LONGBOW_TEST_CASE_EXPECTS(Global, parcNetwork_LinkAddress_Parse_BadMixOfDotsAndDashes, .event = &LongBowTrapIllegalValue) +{ + char *expected = "link://012345.67-89-ab"; + parcNetwork_ParseLinkAddress(expected); +} + +LONGBOW_TEST_CASE(Global, parseMAC48Address) +{ + PARCBuffer *expected = parcBuffer_Wrap((uint8_t []) { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab }, 6, 0, 6); + + PARCBuffer *buffer = parcBuffer_Allocate(7); + bool actual = parcNetwork_ParseMAC48Address("01-23-45-67-89-ab", buffer); + assertTrue(actual, "Expected parcNetwork_ParseMAC48Address() to return true"); + + parcBuffer_Flip(buffer); + + assertTrue(parcBuffer_Equals(expected, buffer), "Expected buffer contents failed."); + + parcBuffer_Release(&expected); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcNetwork_ParseMAC48Address_Colons) +{ + PARCBuffer *expected = parcBuffer_Wrap((uint8_t []) { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab }, 6, 0, 6); + + PARCBuffer *buffer = parcBuffer_Allocate(7); + bool actual = parcNetwork_ParseMAC48Address("01:23:45:67:89:ab", buffer); + assertTrue(actual, "Expected parcNetwork_ParseMAC48Address() to return true"); + parcBuffer_Flip(buffer); + + assertTrue(parcBuffer_Equals(expected, buffer), "Expected buffer contents failed."); + + parcBuffer_Release(&expected); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcNetwork_ParseMAC48Address_Colons_TooShort) +{ + PARCBuffer *expected = parcBuffer_Wrap((uint8_t []) { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab }, 6, 0, 6); + + PARCBuffer *buffer = parcBuffer_Allocate(7); + bool actual = parcNetwork_ParseMAC48Address("01:23:45:67:89", buffer); + assertFalse(actual, "Expected parcNetwork_ParseMAC48Address() to return false"); + assertTrue(parcBuffer_Position(buffer) == 0, "Expected buffer to be unmodified."); + + parcBuffer_Release(&expected); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcNetwork_ParseMAC48Address_Colons_Garbage) +{ + PARCBuffer *expected = parcBuffer_Wrap((uint8_t []) { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab }, 6, 0, 6); + + PARCBuffer *buffer = parcBuffer_Allocate(7); + bool actual = parcNetwork_ParseMAC48Address("0x:23:45:67:89:ab", buffer); + assertFalse(actual, "Expected parcNetwork_ParseMAC48Address() to return false"); + assertTrue(parcBuffer_Position(buffer) == 0, "Expected the PARCBuffer to be unchanged."); + + parcBuffer_Release(&expected); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcNetwork_ParseMAC48Address) +{ + PARCBuffer *expected = parcBuffer_Wrap((uint8_t []) { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab }, 6, 0, 6); + + PARCBuffer *buffer = parcBuffer_Allocate(7); + bool actual = parcNetwork_ParseMAC48Address("0123.4567.89ab", buffer); + assertTrue(actual, "Expected _parseLinkAddressDot() to return true"); + parcBuffer_Flip(buffer); + + assertTrue(parcBuffer_Equals(expected, buffer), "Expected buffer contents failed."); + + parcBuffer_Release(&expected); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcNetwork_ParseMAC48Address_TooShort) +{ + PARCBuffer *expected = parcBuffer_Wrap((uint8_t []) { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab }, 6, 0, 6); + + PARCBuffer *buffer = parcBuffer_Allocate(7); + bool actual = parcNetwork_ParseMAC48Address("0123.4567", buffer); + assertFalse(actual, "Expected parcNetwork_ParseMAC48Address() to return false"); + assertTrue(parcBuffer_Position(buffer) == 0, "Expected the PARCBuffer to be unchanged."); + + parcBuffer_Release(&expected); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcNetwork_ParseInet4Address) +{ + struct sockaddr_in *address = parcNetwork_SockInet4Address("127.0.0.1", 1234); + + PARCBufferComposer *composer = parcNetwork_SockInet4Address_BuildString(address, parcBufferComposer_Create()); + + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + char *addressURI = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + + struct sockaddr_in *actual = parcNetwork_ParseInet4Address(addressURI); + + assertTrue(parcNetwork_Inet4Equals(address, actual), + "Expected Addresses are not equal"); + + parcMemory_Deallocate((void **) &actual); + parcMemory_Deallocate((void **) &addressURI); + parcBufferComposer_Release(&composer); + parcMemory_Deallocate((void **) &address); +} + +LONGBOW_TEST_CASE(Global, parcNetwork_Inet4Equals) +{ + struct sockaddr_in *x = parcNetwork_SockInet4Address("127.0.0.1", 1234); + struct sockaddr_in *y = parcNetwork_SockInet4Address("127.0.0.1", 1234); + struct sockaddr_in *z = parcNetwork_SockInet4Address("127.0.0.1", 1234); + struct sockaddr_in *u1 = parcNetwork_SockInet4Address("127.0.0.2", 1234); + struct sockaddr_in *u2 = parcNetwork_SockInet4Address("127.0.0.1", 4567); + + parcObjectTesting_AssertEqualsFunction(parcNetwork_Inet4Equals, x, y, z, u1, u2); + + parcMemory_Deallocate((void **) &x); + parcMemory_Deallocate((void **) &y); + parcMemory_Deallocate((void **) &z); + parcMemory_Deallocate((void **) &u1); + parcMemory_Deallocate((void **) &u2); +} + +LONGBOW_TEST_CASE(Global, parcNetwork_SockAddress_ipv4) +{ + const char *ipv4 = "1.1.1.1"; + unsigned short port = 5959; + + struct sockaddr_in truth = { + .sin_family = PF_INET, + .sin_port = htons(port), + .sin_addr.s_addr = htonl(0x01010101) + }; + + struct sockaddr_in *test = (struct sockaddr_in *) parcNetwork_SockAddress(ipv4, port); + assertNotNull(test, "Got null address for %s port %u", ipv4, port); + assertTrue(truth.sin_family == test->sin_family, "wrong family, expected %d got %d", truth.sin_family, test->sin_family); + assertTrue(truth.sin_port == test->sin_port, "wrong port, expected %u got %u", truth.sin_port, test->sin_port); + + assertTrue(memcmp(&truth.sin_addr, &test->sin_addr, sizeof(struct in_addr)) == 0, "struct in_addr did not compare"); + parcMemory_Deallocate((void **) &test); +} + +LONGBOW_TEST_CASE(Global, parcNetwork_SockAddress_ipv6) +{ + const char *ipv6 = "fe80::aa20:66ff:fe00:314a"; + uint8_t truth_addr[16] = { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0xaa, 0x20, 0x66, 0xff, 0xfe, 0x00, 0x31, 0x4a }; + + unsigned short port = 5959; + + struct sockaddr_in6 truth = { + .sin6_family = PF_INET6, + .sin6_port = htons(port) + }; + + memcpy(&truth.sin6_addr, truth_addr, sizeof(truth.sin6_addr)); + + + struct sockaddr_in6 *test = (struct sockaddr_in6 *) parcNetwork_SockAddress(ipv6, port); + if (test == NULL) { + testSkip("IPv6 is not supported in the runtime environment."); + } + assertTrue(truth.sin6_family == test->sin6_family, "wrong family, expected %d got %d", truth.sin6_family, test->sin6_family); + assertTrue(truth.sin6_port == test->sin6_port, "wrong port, expected %u got %u", truth.sin6_port, test->sin6_port); + + assertTrue(memcmp(&truth.sin6_addr, &test->sin6_addr, sizeof(struct in6_addr)) == 0, "struct in_addr did not compare"); + parcMemory_Deallocate((void **) &test); +} + +LONGBOW_TEST_CASE(Global, parcNetwork_SockAddress_hostname) +{ + const char *name = "localhost"; + unsigned short port = 5959; + + struct sockaddr *test = parcNetwork_SockAddress(name, port); + assertNotNull(test, "Got null looking up '%s'", name); + parcMemory_Deallocate((void **) &test); +} + +LONGBOW_TEST_CASE(Global, parcNetwork_IsSocketLocal_PF_LOCAL) +{ + struct sockaddr_un name; + name.sun_family = PF_LOCAL; + + bool isLocal = parcNetwork_IsSocketLocal((struct sockaddr *) &name); + assertTrue(isLocal, "PF_LOCAL address did not return as local"); +} + +LONGBOW_TEST_CASE(Global, parcNetwork_IsSocketLocal_PF_IPX) +{ + struct sockaddr_un name; + name.sun_family = PF_IPX; + + bool isLocal = parcNetwork_IsSocketLocal((struct sockaddr *) &name); + assertFalse(isLocal, "Expected parcNetwork_IsSocketLocal(PF_PUP) to return false"); +} + +LONGBOW_TEST_CASE(Global, parcNetwork_IsSocketLocal_PF_INET4) +{ + struct sockaddr *s = parcNetwork_SockAddress("127.1.1.1", 5900); + bool isLoopback = parcNetwork_IsSocketLocal(s); + assertTrue(isLoopback, "127.1.1.1 should be called loopback"); + parcMemory_Deallocate((void **) &s); +} + +LONGBOW_TEST_CASE(Global, parcNetwork_IsSocketLocal_PF_INET6) +{ + struct sockaddr *s = parcNetwork_SockAddress("::1", 5900); + bool isLoopback = parcNetwork_IsSocketLocal(s); + assertTrue(isLoopback, "::1 should be called loopback"); + parcMemory_Deallocate((void **) &s); +} + + +// ======================================================================= + +LONGBOW_TEST_FIXTURE(Local) +{ + LONGBOW_RUN_TEST_CASE(Local, parcNetwork_IsInet6Local_True); + LONGBOW_RUN_TEST_CASE(Local, parcNetwork_IsInet6Local_False); + LONGBOW_RUN_TEST_CASE(Local, parcNetwork_IsInet4Local_True); + LONGBOW_RUN_TEST_CASE(Local, parcNetwork_IsInet4Local_False); + + LONGBOW_RUN_TEST_CASE(Local, _parseMAC48AddressDashOrColon); + LONGBOW_RUN_TEST_CASE(Local, _parseMAC48AddressDashOrColon_Colons); + LONGBOW_RUN_TEST_CASE(Local, _parseMAC48AddressDot); + LONGBOW_RUN_TEST_CASE(Local, _parseMAC48AddressDot_TooShort); + LONGBOW_RUN_TEST_CASE(Local, _parseMAC48AddressDashOrColon_Colons_TooShort); + LONGBOW_RUN_TEST_CASE(Local, _parseMAC48AddressDashOrColon_Colons_Garbage); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Local, parcNetwork_IsInet6Local_True) +{ + struct sockaddr *s = parcNetwork_SockAddress("::1", 5900); + + bool isLoopback = _isInet6Loopback((struct sockaddr_in6 *) s); + assertTrue(isLoopback, "::1 should be called loopback"); + parcMemory_Deallocate((void **) &s); +} + +LONGBOW_TEST_CASE(Local, parcNetwork_IsInet6Local_False) +{ + struct sockaddr *s = parcNetwork_SockAddress("fe80::aa20:66ff:fe00:1", 5900); + + bool isLoopback = _isInet6Loopback((struct sockaddr_in6 *) s); + assertFalse(isLoopback, "fe80::aa20:66ff:fe00:1 should not be called loopback"); + parcMemory_Deallocate((void **) &s); +} + +LONGBOW_TEST_CASE(Local, parcNetwork_IsInet4Local_True) +{ + struct sockaddr *s = parcNetwork_SockAddress("127.1.1.1", 5900); + + bool isLoopback = _isInet4Loopback((struct sockaddr_in *) s); + assertTrue(isLoopback, "127.1.1.1 should be called loopback"); + parcMemory_Deallocate((void **) &s); +} + +LONGBOW_TEST_CASE(Local, parcNetwork_IsInet4Local_False) +{ + struct sockaddr *s = parcNetwork_SockAddress("13.1.1.1", 5900); + + bool isLoopback = _isInet4Loopback((struct sockaddr_in *) s); + assertFalse(isLoopback, "13.1.1.1 should not be called loopback"); + parcMemory_Deallocate((void **) &s); +} + +LONGBOW_TEST_CASE(Local, _parseMAC48AddressDashOrColon) +{ + PARCBuffer *expected = parcBuffer_Wrap((uint8_t []) { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab }, 6, 0, 6); + + PARCBuffer *buffer = parcBuffer_Allocate(7); + PARCBuffer *actual = _parseMAC48AddressDashOrColon("01-23-45-67-89-ab", buffer); + assertNotNull(actual, "Expected _parseLinkAddressDashOrColon() to return non-NULL value"); + parcBuffer_Flip(actual); + + assertTrue(parcBuffer_Equals(expected, actual), "Expected buffer contents failed."); + + parcBuffer_Release(&expected); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Local, _parseMAC48AddressDashOrColon_Colons) +{ + PARCBuffer *expected = parcBuffer_Wrap((uint8_t []) { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab }, 6, 0, 6); + + PARCBuffer *buffer = parcBuffer_Allocate(7); + PARCBuffer *actual = _parseMAC48AddressDashOrColon("01:23:45:67:89:ab", buffer); + assertNotNull(actual, "Expected _parseLinkAddressDashOrColon() to return non-NULL value"); + parcBuffer_Flip(actual); + + assertTrue(parcBuffer_Equals(expected, actual), "Expected buffer contents failed."); + + parcBuffer_Release(&expected); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Local, _parseMAC48AddressDashOrColon_Colons_TooShort) +{ + PARCBuffer *expected = parcBuffer_Wrap((uint8_t []) { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab }, 6, 0, 6); + + PARCBuffer *buffer = parcBuffer_Allocate(7); + PARCBuffer *actual = _parseMAC48AddressDashOrColon("01:23:45:67:89", buffer); + assertNull(actual, "Expected _parseLinkAddressDashOrColon() to return NULL value"); + + parcBuffer_Release(&expected); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Local, _parseMAC48AddressDashOrColon_Colons_Garbage) +{ + PARCBuffer *expected = parcBuffer_Wrap((uint8_t []) { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab }, 6, 0, 6); + + PARCBuffer *buffer = parcBuffer_Allocate(7); + PARCBuffer *actual = _parseMAC48AddressDashOrColon("0x:23:45:67:89:ab", buffer); + assertNull(actual, "Expected _parseLinkAddressDashOrColon() to return NULL value"); + assertTrue(parcBuffer_Position(buffer) == 0, "Expected the PARCBuffer to be unchanged."); + + parcBuffer_Release(&expected); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Local, _parseMAC48AddressDot) +{ + PARCBuffer *expected = parcBuffer_Wrap((uint8_t []) { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab }, 6, 0, 6); + + PARCBuffer *buffer = parcBuffer_Allocate(7); + PARCBuffer *actual = _parseMAC48AddressDot("0123.4567.89ab", buffer); + assertNotNull(actual, "Expected _parseLinkAddressDot() to return non-NULL value"); + parcBuffer_Flip(actual); + + assertTrue(parcBuffer_Equals(expected, actual), "Expected buffer contents failed."); + + parcBuffer_Release(&expected); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Local, _parseMAC48AddressDot_TooShort) +{ + PARCBuffer *expected = parcBuffer_Wrap((uint8_t []) { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab }, 6, 0, 6); + + PARCBuffer *buffer = parcBuffer_Allocate(7); + PARCBuffer *actual = _parseMAC48AddressDot("0123.4567", buffer); + assertNull(actual, "Expected _parseLinkAddressDot() to return NULL value"); + + parcBuffer_Release(&expected); + parcBuffer_Release(&buffer); +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_Networking); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_Object.c b/libparc/parc/algol/test/test_parc_Object.c new file mode 100755 index 00000000..687654fe --- /dev/null +++ b/libparc/parc/algol/test/test_parc_Object.c @@ -0,0 +1,1580 @@ +/* + * 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 "../parc_Object.c" + +#include <inttypes.h> +#include <sys/time.h> + +#include <LongBow/unit-test.h> + +#include <parc/testing/parc_ObjectTesting.h> +#include <parc/testing/parc_MemoryTesting.h> + +#include <parc/algol/parc_SafeMemory.h> +#include <parc/algol/parc_StdlibMemory.h> + +typedef struct { + int calledCount; + int val; +} _dummy_object; + +typedef _dummy_object _DummyObject; + +static void +_dummy_Destroy(_DummyObject **obj __attribute__((unused))) +{ +} + +static _DummyObject * +_dummy_Copy(const _DummyObject *obj); + + +static bool +_dummy_Equals(const _DummyObject *x, const _DummyObject *y) +{ + const _DummyObject *dummy1 = x; + const _DummyObject *dummy2 = y; + return (dummy1->calledCount == dummy2->calledCount); +} + +static int +_dummy_Compare(const _DummyObject *x, const _DummyObject *y) +{ + const _DummyObject *dummy1 = x; + const _DummyObject *dummy2 = y; + if (dummy1->calledCount == dummy2->calledCount) { + return 0; + } else if (dummy1->calledCount < dummy2->calledCount) { + return -1; + } else { + return 1; + } +} + +static uint32_t +_dummy_HashCode(const _DummyObject *obj) +{ + _DummyObject *dummy = (_DummyObject *) obj; + dummy->calledCount++; + return 1337; +} + +static char * +_dummy_ToString(const _DummyObject *x __attribute__((unused))) +{ + char *str = (char *) parcMemory_Allocate(6); + char *test = "dummy"; + sprintf(str, "%s", test); + return str; +} + +static PARCJSON * +_dummy_ToJSON(const _DummyObject *x __attribute__((unused))) +{ + PARCJSON *json = parcJSON_ParseString("{ \"type\" : \"dummy\" }"); + return json; +} + +parcObject_Override(_DummyObject, PARCObject, + .destroy = (PARCObjectDestroy *) _dummy_Destroy, + .copy = (PARCObjectCopy *) _dummy_Copy, + .toString = (PARCObjectToString *) _dummy_ToString, + .equals = (PARCObjectEquals *) _dummy_Equals, + .compare = (PARCObjectCompare *) _dummy_Compare, + .hashCode = (PARCObjectHashCode *) _dummy_HashCode, + .toJSON = (PARCObjectToJSON *) _dummy_ToJSON); + +static _DummyObject * +_dummy_Copy(const _DummyObject *obj) +{ + _DummyObject *newDummy = parcObject_CreateInstance(_DummyObject); + const _DummyObject *dummy = obj; + newDummy->calledCount = dummy->calledCount; + return newDummy; +} + +typedef _dummy_object _DummyObjectNoHash; +parcObject_ExtendPARCObject(_DummyObjectNoHash, + _dummy_Destroy, + _dummy_Copy, + _dummy_ToString, + _dummy_Equals, + _dummy_Compare, + NULL, + _dummy_ToJSON); + +static bool +_meta_destructor_true(PARCObject **objPtr) +{ + return true; +} + +static bool +_meta_destructor_false(PARCObject **objPtr) +{ + (*objPtr) = NULL; + return false; +} + +static PARCObject * +_meta_copy(const PARCObject *ptr) +{ + _DummyObject *d = parcMemory_AllocateAndClear(sizeof(_DummyObject)); + _DummyObject *xx = (_DummyObject *) ptr; + d->val = xx->val; + return d; +} + +static bool +_meta_equals(const PARCObject *x, const PARCObject *y) +{ + _DummyObject *xx = (_DummyObject *) x; + _DummyObject *yy = (_DummyObject *) y; + return (xx->val == yy->val); +} + +static int +_meta_compare(const PARCObject *x, const PARCObject *y) +{ + _DummyObject *xx = (_DummyObject *) x; + _DummyObject *yy = (_DummyObject *) y; + + if (xx->val == yy->val) { + return 0; + } else if (xx->val < yy->val) { + return -1; + } else { + return 1; + } +} + +static PARCHashCode +_meta_hashCode(const PARCObject *ptr) +{ + _DummyObject *xx = (_DummyObject *) ptr; + return xx->val; +} + +static char * +_meta_toString(const PARCObject *ptr) +{ + _DummyObject *xx = (_DummyObject *) ptr; + + char *result; + parcMemory_MemAlign((void **) &result, sizeof(void *), sizeof(char)); + assertNotNull(result, "parcMemory_Allocate returned NULL"); + + char *p = result; + sprintf(result, "%d", xx->val); + + return p; +} + +static PARCJSON * +_meta_toJson(const PARCObject *ptr) +{ + _DummyObject *xx = (_DummyObject *) ptr; + + PARCJSON *json = parcJSON_Create(); + parcJSON_AddInteger(json, "value", xx->val); + + return json; +} + +static const PARCMemoryInterface *_originalMemoryProvider; + +LONGBOW_TEST_RUNNER(parcObject) +{ + LONGBOW_RUN_TEST_FIXTURE(Performance); + LONGBOW_RUN_TEST_FIXTURE(PARCObjectDescriptor); + LONGBOW_RUN_TEST_FIXTURE(Static); + LONGBOW_RUN_TEST_FIXTURE(StaticObjects); + LONGBOW_RUN_TEST_FIXTURE(Meta); + LONGBOW_RUN_TEST_FIXTURE(AcquireRelease); + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Subclasses); + LONGBOW_RUN_TEST_FIXTURE(Fail); + LONGBOW_RUN_TEST_FIXTURE(Locking); + LONGBOW_RUN_TEST_FIXTURE(WaitNotify); + LONGBOW_RUN_TEST_FIXTURE(Synchronization); +} + +LONGBOW_TEST_RUNNER_SETUP(parcObject) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_RUNNER_TEARDOWN(parcObject) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Static) +{ + LONGBOW_RUN_TEST_CASE(Static, _objectHeaderIsValid); + LONGBOW_RUN_TEST_CASE(Static, _parcObject_PrefixLength); +} + +LONGBOW_TEST_FIXTURE_SETUP(Static) +{ + _originalMemoryProvider = parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Static) +{ + parcMemory_SetInterface(_originalMemoryProvider); + return LONGBOW_STATUS_SUCCEEDED; +} + +struct timeval _testObject; + +parcObject_Override(_testObject, PARCObject); + +LONGBOW_TEST_CASE(Static, _objectHeaderIsValid) +{ + PARCObject *object = parcObject_CreateInstanceImpl(&_testObject_Descriptor); + + _PARCObjectHeader *header = _parcObject_Header(object); + + assertTrue(_parcObjectHeader_IsValid(header, object), "Expected _parcObject_HeaderHeaderIsValid to be valid"); + + parcObject_Release(&object); +} + +LONGBOW_TEST_CASE(Static, _parcObject_PrefixLength) +{ + // Test that the result is a multiple of the alignment value and greater than the size of _PARCObjectHeader. + + // Compute the power of 2 value of sizeof(void *) + unsigned int v = sizeof(void *); + unsigned int r = 0; // r will be lg(v) + + while (v >>= 1) { + r++; + } + PARCObjectDescriptor descriptor; + + for (int i = r; i < 20; i++) { + descriptor.objectAlignment = 1 << i; + size_t actual = _parcObject_PrefixLength(&descriptor); + assertTrue((actual & (descriptor.objectAlignment - 1)) == 0, + "Alignment needs to be a multiple of %u", descriptor.objectAlignment); + } +} + +LONGBOW_TEST_FIXTURE(AcquireRelease) +{ + LONGBOW_RUN_TEST_CASE(AcquireRelease, parcObject_Acquire); + LONGBOW_RUN_TEST_CASE(AcquireRelease, parcObject_Release); +// LONGBOW_RUN_TEST_CASE(AcquireRelease, parcObject_Acquire_Invalid); +} + +LONGBOW_TEST_FIXTURE_SETUP(AcquireRelease) +{ + _originalMemoryProvider = parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(AcquireRelease) +{ + parcSafeMemory_ReportAllocation(STDOUT_FILENO); + parcMemory_SetInterface(_originalMemoryProvider); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(AcquireRelease, parcObject_Acquire) +{ + struct timeval *expected = parcObject_CreateInstanceImpl(&_testObject_Descriptor); + + parcObjectTesting_AssertAcquireReleaseContract(parcObject_Acquire, expected); + parcObject_Release((void **) &expected); +} + +LONGBOW_TEST_CASE(AcquireRelease, parcObject_Release) +{ + struct timeval *time = parcObject_CreateInstanceImpl(&_testObject_Descriptor); + parcObject_AssertValid(time); + + time->tv_sec = 1; + time->tv_usec = 2; + + PARCReferenceCount count = parcObject_Release((PARCObject **) &time); + assertTrue(count == 0, "Expected reference count to be zero"); + assertTrue(time == 0, "Expected memory pointer to be NULL after destroy."); +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcObject_Create); +// LONGBOW_RUN_TEST_CASE(Global, parcObject_CreateAndClear); + LONGBOW_RUN_TEST_CASE(Global, parcObject_IsValid); + LONGBOW_RUN_TEST_CASE(Global, parcObject_IsValid_NotValid); + LONGBOW_RUN_TEST_CASE(Global, parcObject_IsInstanceOf); + LONGBOW_RUN_TEST_CASE(Global, parcObject_Copy_Default); + LONGBOW_RUN_TEST_CASE(Global, parcObject_Copy); + LONGBOW_RUN_TEST_CASE(Global, parcObject_Release); + LONGBOW_RUN_TEST_CASE(Global, parcObject_Compare_Default); + LONGBOW_RUN_TEST_CASE(Global, parcObject_Compare_NoOverride); + LONGBOW_RUN_TEST_CASE(Global, parcObject_Compare); + LONGBOW_RUN_TEST_CASE(Global, parcObject_Equals_Default); + LONGBOW_RUN_TEST_CASE(Global, parcObject_Equals_NoOverride); + LONGBOW_RUN_TEST_CASE(Global, parcObject_Equals); + LONGBOW_RUN_TEST_CASE(Global, parcObject_HashCode_Default); + LONGBOW_RUN_TEST_CASE(Global, parcObject_HashCode_NoOverride); + LONGBOW_RUN_TEST_CASE(Global, parcObject_HashCode); + LONGBOW_RUN_TEST_CASE(Global, parcObject_ToString_Default); + LONGBOW_RUN_TEST_CASE(Global, parcObject_ToString_NoOverride); + LONGBOW_RUN_TEST_CASE(Global, parcObject_ToString); + LONGBOW_RUN_TEST_CASE(Global, parcObject_ToJSON_Default); + LONGBOW_RUN_TEST_CASE(Global, parcObject_ToJSON_NoOverride); + LONGBOW_RUN_TEST_CASE(Global, parcObject_ToJSON); + LONGBOW_RUN_TEST_CASE(Global, parcObject_GetReferenceCount); + LONGBOW_RUN_TEST_CASE(Global, parcObject_Display_Default); + LONGBOW_RUN_TEST_CASE(Global, parcObject_Display_NoOverride); + LONGBOW_RUN_TEST_CASE(Global, parcObject_Display); + LONGBOW_RUN_TEST_CASE(Global, parcObject_GetDescriptor); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + _originalMemoryProvider = parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + + parcMemory_SetInterface(_originalMemoryProvider); + + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestRunner_GetName(testRunner), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcObject_Release) +{ + struct timeval *time = parcObject_CreateInstanceImpl(&_testObject_Descriptor); + parcObject_AssertValid(time); + + time->tv_sec = 1; + time->tv_usec = 2; + + PARCReferenceCount count = parcObject_Release((PARCObject **) &time); + assertTrue(count == 0, "Expected reference count to be zero"); + assertTrue(time == 0, "Expected memory pointer to be NULL after destroy."); +} + +LONGBOW_TEST_CASE(Global, parcObject_Create) +{ + struct timeval *time = parcObject_CreateInstanceImpl(&_testObject_Descriptor); + parcObject_AssertValid(time); + + time->tv_sec = 1; + time->tv_usec = 2; + + PARCReferenceCount count = parcObject_Release((PARCObject **) &time); + assertTrue(count == 0, "Expected reference count to be zero"); + assertTrue(time == 0, "Expected memory pointer to be NULL after destroy."); +} + +//LONGBOW_TEST_CASE(Global, parcObject_CreateAndClear) +//{ +// struct timeval *time = parcObject_CreateAndClear(struct timeval); +// parcObject_AssertValid(time); +// +// time->tv_sec = 1; +// time->tv_usec = 2; +// +// PARCReferenceCount count = parcObject_Release((PARCObject **) &time); +// assertTrue(count == 0, "Expected reference count to be zero"); +// assertTrue(time == 0, "Expected memory pointer to be NULL after destroy."); +//} + +LONGBOW_TEST_CASE(Global, parcObject_IsValid) +{ + PARCObject *object = parcObject_CreateInstanceImpl(&_testObject_Descriptor); + assertTrue(parcObject_IsValid(object), "Expected valid PARCObject"); + + parcObject_Release(&object); +} + +LONGBOW_TEST_CASE(Global, parcObject_IsInstanceOf) +{ + _DummyObject *dummy1 = parcObject_CreateInstance(_DummyObject); + assertTrue(parcObject_IsInstanceOf(dummy1, &PARCObject_Descriptor), + "Expected _DummyObject to be an instance of PARCObject"); + + parcObject_Release((PARCObject **) &dummy1); +} + +LONGBOW_TEST_CASE(Global, parcObject_IsValid_NotValid) +{ + PARCObject *object = parcObject_CreateInstanceImpl(&_testObject_Descriptor); + PARCObject *alias = object; + parcObject_Release(&object); + assertFalse(parcObject_IsValid(object), "Expected invalid PARCObject"); + assertFalse(parcObject_IsValid(alias), "Expected invalid PARCObject"); +} + +LONGBOW_TEST_CASE(Global, parcObject_Copy_Default) +{ + struct timeval *time = parcObject_CreateInstanceImpl(&_testObject_Descriptor); + parcObject_AssertValid(time); + + time->tv_sec = 1; + time->tv_usec = 2; + + struct timeval *copy = parcObject_Copy(time); + + parcObject_AssertValid(copy); + + assertTrue(copy->tv_sec == 1, "Expected tv_sec to equal 1"); + assertTrue(copy->tv_usec == 2, "Expected tv_usec to equal 2"); + + PARCReferenceCount count = parcObject_Release((PARCObject **) ©); + assertTrue(count == 0, "Expected reference count to be zero"); + assertTrue(copy == 0, "Expected memory pointer to be NULL after destroy."); + + parcObject_Release((PARCObject *) &time); +} + +LONGBOW_TEST_CASE(Global, parcObject_Copy) +{ + _DummyObject *dummy1 = parcObject_CreateInstance(_DummyObject); + dummy1->calledCount = 100; + + _DummyObject *dummy2 = parcObject_Copy(dummy1); + + assertTrue(dummy2->calledCount == dummy1->calledCount, + "Expected called counts to be the same. Got %d, expected %d.", dummy1->calledCount, dummy2->calledCount); + + parcObject_Release((PARCObject **) &dummy1); + parcObject_Release((PARCObject **) &dummy2); +} + +LONGBOW_TEST_CASE(Global, parcObject_Compare_Default) +{ + struct timeval *time1 = parcObject_CreateAndClearInstanceImpl(&_testObject_Descriptor); + parcObject_AssertValid(time1); + + time1->tv_sec = 1; + time1->tv_usec = 2; + + struct timeval *time2 = parcObject_CreateAndClearInstanceImpl(&_testObject_Descriptor); + parcObject_AssertValid(time2); + + time2->tv_sec = 1; + time2->tv_usec = 2; + + int compareResult = parcObject_Compare(time1, time2); + assertTrue(compareResult == 0, "Expected objects to compare equal. Comparison result: %d", compareResult); + compareResult = parcObject_Compare(time1, time1); + assertTrue(compareResult == 0, "Expected same object to be equal since they have identical pointer addresses. Comparison result: %d", compareResult); + + parcObject_Release((PARCObject *) &time1); + parcObject_Release((PARCObject *) &time2); +} + +LONGBOW_TEST_CASE(Global, parcObject_Compare_NoOverride) +{ + const PARCObjectDescriptor *descriptor = + parcObjectDescriptor_Create("override", + sizeof(struct timeval), sizeof(void*), + true, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &PARCObject_Descriptor, NULL); + struct timeval *time1 = parcObject_CreateAndClearInstanceImpl(descriptor); + parcObject_AssertValid(time1); + + time1->tv_sec = 1; + time1->tv_usec = 2; + + struct timeval *time2 = parcObject_CreateAndClearInstanceImpl(descriptor); + parcObject_AssertValid(time2); + + time2->tv_sec = 1; + time2->tv_usec = 2; + + int compareResult = parcObject_Compare(time1, time2); + assertTrue(compareResult == 0, "Expected objects to compare equal. Comparison result: %d", compareResult); + compareResult = parcObject_Compare(time1, time1); + assertTrue(compareResult == 0, "Expected same object to be equal since they have identical pointer addresses. Comparison result: %d", compareResult); + + parcObject_Release((PARCObject *) &time1); + parcObject_Release((PARCObject *) &time2); + parcObjectDescriptor_Destroy((PARCObjectDescriptor **) &descriptor); +} + +LONGBOW_TEST_CASE(Global, parcObject_Compare) +{ + _DummyObject *value = parcObject_CreateAndClearInstance(_DummyObject); + _DummyObject *equality[2]; + equality[0] = parcObject_CreateAndClearInstance(_DummyObject); + equality[1] = NULL; + _DummyObject *lesser[2]; + lesser[0] = parcObject_CreateAndClearInstance(_DummyObject); + lesser[1] = NULL; + + _DummyObject *greater[2]; + greater[0] = parcObject_CreateAndClearInstance(_DummyObject); + greater[1] = NULL; + + value->calledCount = 50; + equality[0]->calledCount = 50; + lesser[0]->calledCount = 10; + greater[0]->calledCount = 80; + + parcObjectTesting_AssertCompareTo(parcObject_Compare, value, equality, lesser, greater); + + parcObject_Release((void **) &value); + parcObject_Release((void **) &equality[0]); + parcObject_Release((void **) &lesser[0]); + parcObject_Release((void **) &greater[0]); +} + +LONGBOW_TEST_CASE(Global, parcObject_Equals_Default) +{ + struct timeval *x = parcObject_CreateAndClearInstanceImpl(&_testObject_Descriptor); + memset(x, 0, sizeof(struct timeval)); + x->tv_sec = 1; + x->tv_usec = 2; + + struct timeval *y = parcObject_CreateAndClearInstanceImpl(&_testObject_Descriptor); + memset(y, 0, sizeof(struct timeval)); + y->tv_sec = 1; + y->tv_usec = 2; + + assertTrue(parcObject_Equals(x, y), "Expected equality"); + + struct timeval *z = parcObject_CreateAndClearInstanceImpl(&_testObject_Descriptor); + z->tv_sec = 1; + z->tv_usec = 2; + + struct timeval *unequal1 = parcObject_CreateAndClearInstanceImpl(&_testObject_Descriptor); + unequal1->tv_sec = 1; + unequal1->tv_usec = 1; + + struct timeval *unequal2 = parcObject_CreateAndClearInstanceImpl(&_testObject_Descriptor); + unequal2->tv_sec = 0; + unequal2->tv_usec = 0; + + parcObjectTesting_AssertEqualsFunction(parcObject_Equals, x, y, z, unequal1, unequal2, NULL); + + parcObject_Release((PARCObject *) &x); + parcObject_Release((PARCObject *) &y); + parcObject_Release((PARCObject *) &z); + parcObject_Release((PARCObject *) &unequal1); + parcObject_Release((PARCObject *) &unequal2); +} + +LONGBOW_TEST_CASE(Global, parcObject_Equals_NoOverride) +{ + const PARCObjectDescriptor *descriptor = + parcObjectDescriptor_Create("override", sizeof(struct timeval), sizeof(void*), true, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &PARCObject_Descriptor, NULL); + + struct timeval *x = parcObject_CreateAndClearInstanceImpl(descriptor); + memset(x, 0, sizeof(struct timeval)); + x->tv_sec = 1; + x->tv_usec = 2; + + struct timeval *y = parcObject_CreateAndClearInstanceImpl(descriptor); + memset(y, 0, sizeof(struct timeval)); + y->tv_sec = 1; + y->tv_usec = 2; + + assertTrue(parcObject_Equals(x, y), "Expected equality"); + + struct timeval *z = parcObject_CreateAndClearInstanceImpl(descriptor); + z->tv_sec = 1; + z->tv_usec = 2; + + struct timeval *unequal1 = parcObject_CreateAndClearInstanceImpl(descriptor); + unequal1->tv_sec = 1; + unequal1->tv_usec = 1; + + struct timeval *unequal2 = parcObject_CreateAndClearInstanceImpl(descriptor); + unequal2->tv_sec = 0; + unequal2->tv_usec = 0; + + parcObjectTesting_AssertEqualsFunction(parcObject_Equals, x, y, z, unequal1, unequal2, NULL); + + parcObject_Release((PARCObject *) &x); + parcObject_Release((PARCObject *) &y); + parcObject_Release((PARCObject *) &z); + parcObject_Release((PARCObject *) &unequal1); + parcObject_Release((PARCObject *) &unequal2); + parcObjectDescriptor_Destroy((PARCObjectDescriptor **) &descriptor); +} + +LONGBOW_TEST_CASE(Global, parcObject_Equals) +{ + _DummyObject *x = parcObject_CreateInstance(_DummyObject); + x->calledCount = 100; + _DummyObject *y = parcObject_CreateInstance(_DummyObject); + y->calledCount = 100; + _DummyObject *z = parcObject_CreateInstance(_DummyObject); + z->calledCount = 100; + + _DummyObject *unequal1 = parcObject_CreateInstance(_DummyObject); + unequal1->calledCount = 50; + + PARCObject *unequal2 = parcObject_CreateAndClearInstanceImpl(&_testObject_Descriptor); + + PARCObjectDescriptor dummyMeta2 = parcObject_DescriptorName(_DummyObject); + _DummyObject *unequal3 = parcObject_CreateAndClearInstanceImpl(&dummyMeta2); + unequal3->calledCount = 100; + + PARCObject *unequal4 = parcObject_CreateAndClearInstanceImpl(&_testObject_Descriptor); + + parcObjectTesting_AssertEqualsFunction(parcObject_Equals, x, y, z, unequal1, unequal2, unequal3, unequal4, NULL); + + parcObject_Release((PARCObject **) &x); + parcObject_Release((PARCObject **) &y); + parcObject_Release((PARCObject **) &z); + parcObject_Release((PARCObject **) &unequal1); + parcObject_Release((PARCObject **) &unequal2); + parcObject_Release((PARCObject **) &unequal3); + parcObject_Release((PARCObject **) &unequal4); +} + +LONGBOW_TEST_CASE(Global, parcObject_HashCode_Default) +{ + struct timeval *time = parcObject_CreateAndClearInstanceImpl(&_testObject_Descriptor); + parcObject_AssertValid(time); + + time->tv_sec = 1; + time->tv_usec = 2; + + PARCHashCode hashCode = parcObject_HashCode(time); + PARCHashCode expected = parcHashCode_Hash((void *) time, sizeof(struct timeval)); + assertTrue(hashCode == expected, "Hash codes do not match. Got %" PRIPARCHashCode ", expected %" PRIPARCHashCode ".", hashCode, expected); + + parcObject_Release((PARCObject *) &time); +} + +LONGBOW_TEST_CASE(Global, parcObject_HashCode_NoOverride) +{ + const PARCObjectDescriptor *descriptor = + parcObjectDescriptor_Create("override", sizeof(struct timeval), sizeof(void*), true, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &PARCObject_Descriptor, NULL); + struct timeval *time = parcObject_CreateAndClearInstanceImpl(descriptor); + parcObject_AssertValid(time); + + time->tv_sec = 1; + time->tv_usec = 2; + + PARCHashCode hashCode = parcObject_HashCode(time); + PARCHashCode expected = parcHashCode_Hash((void *) time, sizeof(struct timeval)); + assertTrue(hashCode == expected, "Hash codes do not match. Got %" PRIPARCHashCode ", expected %" PRIPARCHashCode ".", hashCode, expected); + + parcObject_Release((PARCObject *) &time); + parcObjectDescriptor_Destroy((PARCObjectDescriptor **) &descriptor); +} + +LONGBOW_TEST_CASE(Global, parcObject_HashCode) +{ + _DummyObject *dummy = parcObject_CreateInstance(_DummyObject); + + PARCHashCode hashCode = parcObject_HashCode(dummy); + assertTrue(hashCode == 1337, "Expected hashcode to be 1337, got %" PRIPARCHashCode, hashCode); + + parcObject_Release((PARCObject **) &dummy); +} + +LONGBOW_TEST_CASE(Global, parcObject_ToString) +{ + _DummyObject *dummy = parcObject_CreateInstance(_DummyObject); + + char *strRep = parcObject_ToString(dummy); + assertTrue(strcmp(strRep, "dummy") == 0, "Expected 'dummy' string representation, got %s", strRep); + + parcMemory_Deallocate((void **) &strRep); + parcObject_Release((PARCObject **) &dummy); +} + +LONGBOW_TEST_CASE(Global, parcObject_ToString_Default) +{ + _DummyObject *dummy = parcObject_CreateAndClearInstanceImpl(&_DummyObject_Descriptor); + + char *strRep = parcObject_ToString(dummy); + + parcMemory_Deallocate((void **) &strRep); + parcObject_Release((PARCObject **) &dummy); +} + +LONGBOW_TEST_CASE(Global, parcObject_ToString_NoOverride) +{ + const PARCObjectDescriptor *descriptor = + parcObjectDescriptor_Create("override", sizeof(struct timeval), sizeof(void*), true, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &PARCObject_Descriptor, NULL); + _DummyObject *dummy = parcObject_CreateAndClearInstanceImpl(descriptor); + + char *strRep = parcObject_ToString(dummy); + + parcMemory_Deallocate((void **) &strRep); + parcObject_Release((PARCObject **) &dummy); + parcObjectDescriptor_Destroy((PARCObjectDescriptor **) &descriptor); +} + +LONGBOW_TEST_CASE(Global, parcObject_ToJSON_Default) +{ + size_t expectedSize = sizeof(struct timeval); + PARCObject *memory = parcObject_CreateAndClearInstanceImpl(&_testObject_Descriptor); + + PARCJSON *json = parcObject_ToJSON(memory); + + const PARCJSONPair *lengthPair = parcJSON_GetPairByName(json, "objectLength"); + PARCJSONValue *lengthValue = parcJSONPair_GetValue(lengthPair); + uint64_t actualLength = parcJSONValue_GetInteger(lengthValue); + + const PARCJSONPair *alignmentPair = parcJSON_GetPairByName(json, "objectAlignment"); + PARCJSONValue *alignmentValue = parcJSONPair_GetValue(alignmentPair); + int alignment = (int) parcJSONValue_GetInteger(alignmentValue); + + assertTrue(actualLength >= expectedSize, + "Expected length to be >= %zd, actual %" PRIu64 "", expectedSize, actualLength); + assertTrue(alignment == sizeof(void *), "Expected objectAlignment to be %zd, got %d", + sizeof(void *), alignment); + + parcJSON_Release(&json); + parcObject_Release(&memory); +} + +LONGBOW_TEST_CASE(Global, parcObject_ToJSON) +{ + _DummyObject *dummy = parcObject_CreateInstance(_DummyObject); + + PARCJSON *json = parcObject_ToJSON(dummy); + char *strRep = parcJSON_ToString(json); + assertTrue(strcmp(strRep, "{ \"type\" : \"dummy\" }") == 0, "Expected fixed JSON object with a specific string representation, got %s", strRep); + + parcMemory_Deallocate((void **) &strRep); + parcJSON_Release(&json); + parcObject_Release((PARCObject **) &dummy); +} + +LONGBOW_TEST_CASE(Global, parcObject_ToJSON_NoOverride) +{ + const PARCObjectDescriptor *descriptor = + parcObjectDescriptor_Create("override", sizeof(struct timeval), sizeof(void*), true, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &PARCObject_Descriptor, NULL); + + size_t expectedSize = sizeof(struct timeval); + PARCObject *memory = parcObject_CreateAndClearInstanceImpl(descriptor); + + PARCJSON *json = parcObject_ToJSON(memory); + const PARCJSONPair *lengthPair = parcJSON_GetPairByName(json, "objectLength"); + PARCJSONValue *lengthValue = parcJSONPair_GetValue(lengthPair); + uint64_t actualLength = parcJSONValue_GetInteger(lengthValue); + + const PARCJSONPair *alignmentPair = parcJSON_GetPairByName(json, "objectAlignment"); + PARCJSONValue *alignmentValue = parcJSONPair_GetValue(alignmentPair); + int alignment = (int) parcJSONValue_GetInteger(alignmentValue); + + assertTrue(actualLength >= expectedSize, + "Expected length to be >= %zd, actual %" PRIu64 "", expectedSize, actualLength); + assertTrue(alignment == sizeof(void *), "Expected objectAlignment to be %zd, got %d", + sizeof(void *), alignment); + + parcJSON_Release(&json); + parcObject_Release(&memory); + parcObjectDescriptor_Destroy((PARCObjectDescriptor **) &descriptor); +} + +LONGBOW_TEST_CASE(Global, parcObject_GetReferenceCount) +{ + _DummyObject *dummy = parcObject_CreateInstance(_DummyObject); + PARCReferenceCount refCount = parcObject_GetReferenceCount(dummy); + assertTrue(refCount == 1, "Expected reference count to be 1, got %" PRIu64 "", refCount); + parcObject_Release((PARCObject **) &dummy); +} + +LONGBOW_TEST_CASE(Global, parcObject_Display_Default) +{ + _DummyObject *dummy = parcObject_CreateAndClearInstanceImpl(&_DummyObject_Descriptor); + parcObject_Display(dummy, 0); + parcObject_Release((PARCObject **) &dummy); +} + +LONGBOW_TEST_CASE(Global, parcObject_Display_NoOverride) +{ + const PARCObjectDescriptor *descriptor = + parcObjectDescriptor_Create("override", sizeof(struct timeval), sizeof(void*), true, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &PARCObject_Descriptor, NULL); + + _DummyObject *dummy = parcObject_CreateAndClearInstanceImpl(descriptor); + parcObject_Display(dummy, 0); + parcObject_Release((PARCObject **) &dummy); + parcObjectDescriptor_Destroy((PARCObjectDescriptor **) &descriptor); +} + +LONGBOW_TEST_CASE(Global, parcObject_Display) +{ + _DummyObject *dummy = parcObject_CreateInstance(_DummyObject); + parcObject_Display(dummy, 0); + parcObject_Release((PARCObject **) &dummy); +} + +LONGBOW_TEST_CASE(Global, parcObject_GetDescriptor) +{ + _DummyObject *dummy = parcObject_CreateInstance(_DummyObject); + const PARCObjectDescriptor *descriptor = parcObject_GetDescriptor(dummy); + + assertTrue(descriptor == &_DummyObject_Descriptor, "Expected pointer to _DummyObject_Descriptor"); + + parcObject_Release((PARCObject **) &dummy); +} + +LONGBOW_TEST_FIXTURE(Subclasses) +{ + LONGBOW_RUN_TEST_CASE(Subclasses, parcObject_Copy); +} + +LONGBOW_TEST_FIXTURE_SETUP(Subclasses) +{ + _originalMemoryProvider = parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Subclasses) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + parcMemory_SetInterface(_originalMemoryProvider); + + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestRunner_GetName(testRunner), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Subclasses, parcObject_Copy) +{ + const PARCObjectDescriptor *objectType = + parcObjectDescriptor_Create("Dummy", sizeof(_DummyObject), sizeof(void*), true, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &PARCObject_Descriptor, NULL); + + _DummyObject *dummy = parcObject_CreateInstance(_DummyObject); + parcObject_SetDescriptor(dummy, objectType); + + dummy->calledCount = 100; + + _DummyObject *dummy2 = parcObject_Copy(dummy); + + assertTrue(dummy2->calledCount == dummy->calledCount, + "Expected called counts to be the same. Got %d, expected %d.", dummy->calledCount, dummy2->calledCount); + + parcObject_Release((PARCObject **) &dummy); + parcObject_Release((PARCObject **) &dummy2); + parcObjectDescriptor_Destroy((PARCObjectDescriptor **) &objectType); +} + +LONGBOW_TEST_FIXTURE(Locking) +{ + LONGBOW_RUN_TEST_CASE(Locking, parcObject_TryLock_Unlock); + LONGBOW_RUN_TEST_CASE(Locking, parcObject_Lock_Unlock); + LONGBOW_RUN_TEST_CASE(Locking, parcObject_TryLock_AlreadyLockedSameThread); + LONGBOW_RUN_TEST_CASE(Locking, parcObject_Lock_AlreadyLocked); +} +static uint32_t initialAllocations; + +LONGBOW_TEST_FIXTURE_SETUP(Locking) +{ + _originalMemoryProvider = parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + initialAllocations = parcMemory_Outstanding(); + + _DummyObject *dummy = parcObject_CreateInstance(_DummyObject); + + longBowTestCase_SetClipBoardData(testCase, dummy); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Locking) +{ + _DummyObject *dummy = longBowTestCase_GetClipBoardData(testCase); + + parcObject_Release((PARCObject **) &dummy); + + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s mismanaged memory.", longBowTestCase_GetFullName(testCase))) { + parcSafeMemory_ReportAllocation(STDOUT_FILENO); + parcMemory_SetInterface(_originalMemoryProvider); + return LONGBOW_STATUS_MEMORYLEAK; + } + parcMemory_SetInterface(_originalMemoryProvider); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Locking, parcObject_TryLock_Unlock) +{ + _DummyObject *dummy = longBowTestCase_GetClipBoardData(testCase); + + bool actual = parcObject_TryLock(dummy); + + assertTrue(actual, "Expected parcObject_TryLock to succeed."); + + actual = parcObject_IsLocked(dummy); + assertTrue(actual, "Expected parcObject_IsLocked to be true."); + + actual = parcObject_Unlock(dummy); + assertTrue(actual, "Expected parcObject_Unlock to succeed."); + + actual = parcObject_IsLocked(dummy); + assertFalse(actual, "Expected parcObject_IsLocked to be false."); +} + +LONGBOW_TEST_CASE(Locking, parcObject_Lock_Unlock) +{ + _DummyObject *dummy = longBowTestCase_GetClipBoardData(testCase); + + bool actual = parcObject_Lock(dummy); + + assertTrue(actual, "Expected parcObject_Lock to succeed."); + + actual = parcObject_IsLocked(dummy); + assertTrue(actual, "Expected parcObject_IsLocked to be true."); + + actual = parcObject_Unlock(dummy); + assertTrue(actual, "Expected parcObject_Unlock to succeed."); + + actual = parcObject_IsLocked(dummy); + assertFalse(actual, "Expected parcObject_IsLocked to be false."); +} + +LONGBOW_TEST_CASE_EXPECTS(Locking, parcObject_TryLock_AlreadyLockedSameThread, .event = &LongBowTrapCannotObtainLockEvent) +{ + _DummyObject *dummy = longBowTestCase_GetClipBoardData(testCase); + + bool actual = parcObject_TryLock(dummy); + + assertTrue(actual, "Expected parcObject_TryLock to succeed."); + + actual = parcObject_TryLock(dummy); + + assertFalse(actual, "Expected parcObject_TryLock to fail when already locked by the same thread."); + + actual = parcObject_Unlock(dummy); + assertTrue(actual, "Expected parcObject_Unlock to succeed."); +} + +LONGBOW_TEST_CASE_EXPECTS(Locking, parcObject_Lock_AlreadyLocked, .event = &LongBowTrapCannotObtainLockEvent) +{ + _DummyObject *dummy = longBowTestCase_GetClipBoardData(testCase); + + bool actual = parcObject_Lock(dummy); + + assertTrue(actual, "Expected parcObject_Lock to succeed."); + + actual = parcObject_IsLocked(dummy); + assertTrue(actual, "Expected locked object to indicate being locked."); + + parcObject_Lock(dummy); +} + +LONGBOW_TEST_FIXTURE(WaitNotify) +{ + LONGBOW_RUN_TEST_CASE(WaitNotify, parcObject_WaitNotify); + LONGBOW_RUN_TEST_CASE(WaitNotify, parcObject_WaitNotify2); + LONGBOW_RUN_TEST_CASE(WaitNotify, parcObject_WaitUntil); + LONGBOW_RUN_TEST_CASE(WaitNotify, parcObject_WaitFor); + LONGBOW_RUN_TEST_CASE(WaitNotify, parcObject_WaitNotifyAll); +} + +LONGBOW_TEST_FIXTURE_SETUP(WaitNotify) +{ + _originalMemoryProvider = parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(WaitNotify) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + + parcMemory_SetInterface(_originalMemoryProvider); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestRunner_GetName(testRunner), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +// Just wait until told to wakeup, then increment a counter and unlock. +static void * +waiter(void *data) +{ + _DummyObject *dummy = data; + + while (parcObject_TryLock(dummy) == false) { + ; + } + assertTrue(parcObject_IsLocked(dummy), "%p object %p not locked.", (void *) pthread_self(), (void *) dummy); + parcObject_Wait(dummy); + + dummy->val++; + parcObject_Unlock(dummy); + + return data; +} + +LONGBOW_TEST_CASE(WaitNotify, parcObject_WaitNotify) +{ + _DummyObject *dummy = parcObject_CreateInstance(_DummyObject); + + dummy->val = 0; + + pthread_t thread_A; + pthread_t thread_B; + pthread_t thread_C; + pthread_create(&thread_A, NULL, waiter, dummy); + pthread_create(&thread_B, NULL, waiter, dummy); + pthread_create(&thread_C, NULL, waiter, dummy); + + while (dummy->val != 3) { + while (parcObject_TryLock(dummy) == false) { + ; + } + parcObject_Notify(dummy); + parcObject_Unlock(dummy); + } + + pthread_join(thread_A, NULL); +// pthread_join(thread_B, NULL); +// pthread_join(thread_C, NULL); + + parcObject_Release((PARCObject **) &dummy); +} + +LONGBOW_TEST_CASE(WaitNotify, parcObject_WaitNotifyAll) +{ + _DummyObject *dummy = parcObject_CreateInstance(_DummyObject); + + dummy->val = 0; + + pthread_t thread_A; + pthread_t thread_B; + pthread_t thread_C; + pthread_create(&thread_A, NULL, waiter, dummy); + pthread_create(&thread_B, NULL, waiter, dummy); + pthread_create(&thread_C, NULL, waiter, dummy); + + while (dummy->val != 3) { + while (parcObject_TryLock(dummy) == false) { + ; + } + parcObject_NotifyAll(dummy); + parcObject_Unlock(dummy); + } + + pthread_join(thread_A, NULL); +// pthread_join(thread_B, NULL); +// pthread_join(thread_C, NULL); + + assertTrue(dummy->val == 3, "Expected the counter to be 3, actual %d", dummy->val); + + parcObject_Release((PARCObject **) &dummy); +} + +static void * +decrement(void *data) +{ + _DummyObject *dummy = data; + + while (parcObject_TryLock(dummy) == false) { + ; + } + while (dummy->val < 12) { + parcObject_Wait(dummy); + dummy->val--; + } + parcObject_Unlock(dummy); + + return data; +} + +LONGBOW_TEST_CASE(WaitNotify, parcObject_WaitNotify2) +{ + _DummyObject *dummy = parcObject_CreateInstance(_DummyObject); + + dummy->val = 0; + + pthread_t thread_A; + pthread_create(&thread_A, NULL, decrement, dummy); + + dummy->val = 2; + while (parcObject_TryLock(dummy) == false) { + ; + } + while (dummy->val <= 12) { + parcObject_Notify(dummy); + dummy->val += 2; + } + parcObject_Unlock(dummy); + + pthread_join(thread_A, NULL); + + parcObject_Release((PARCObject **) &dummy); +} + +LONGBOW_TEST_CASE(WaitNotify, parcObject_WaitUntil) +{ + _DummyObject *dummy = parcObject_CreateInstance(_DummyObject); + + dummy->val = 0; + + time_t now; + time_t then; + time(&then); + if (parcObject_Lock(dummy)) { + struct timespec future; + future.tv_sec = then + 3; + future.tv_nsec = 0; + parcObject_WaitUntil(dummy, &future); + + time(&now); + long expected = now - 1; // Subtract 1 because the future may have been computed at the 999,999,999 nanosecond mark. + assertTrue(now >= expected, "Expected now %ld, to be later than than %ld", now, expected); + parcObject_Unlock(dummy); + } + + parcObject_Release((PARCObject **) &dummy); +} + +LONGBOW_TEST_CASE(WaitNotify, parcObject_WaitFor) +{ + _DummyObject *dummy = parcObject_CreateInstance(_DummyObject); + + dummy->val = 0; + + time_t now; + time_t then; + time(&then); + if (parcObject_Lock(dummy)) { + uint64_t nanoSeconds = 1000000000; + parcObject_WaitFor(dummy, nanoSeconds); + + time(&now); + now++; // Advance now by 1 because of the precision mismatch between gettimeofday and nanosecond resolution of parcObject_WaitFor + assertTrue(now >= then + (nanoSeconds / 1000000000), + "Expected now %ld, to be later than time %" PRIu64, now, then + (nanoSeconds / 1000000000)); + parcObject_Unlock(dummy); + } + + parcObject_Release((PARCObject **) &dummy); +} + +LONGBOW_TEST_FIXTURE(Fail) +{ +} + +typedef struct { + PARCObject *value; +} TestData; + +LONGBOW_TEST_FIXTURE_SETUP(Fail) +{ + _originalMemoryProvider = parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + data->value = parcObject_CreateInstance(_DummyObjectNoHash); + if (data->value == NULL) { + return LONGBOW_STATUS_SETUP_FAILED; + } + longBowTestCase_SetClipBoardData(testCase, data); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Fail) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + parcObject_Release(&data->value); + parcMemory_Deallocate((void **) &data); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + + parcMemory_SetInterface(_originalMemoryProvider); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestRunner_GetName(testRunner), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_OPTIONS(Performance, .enabled = false) +{ + LONGBOW_RUN_TEST_CASE(Performance, _parcObject_PrefixLength_10000000); + LONGBOW_RUN_TEST_CASE(Performance, parcObject_CreateRelease); + LONGBOW_RUN_TEST_CASE(Performance, parcObject_Create); + LONGBOW_RUN_TEST_CASE(Performance, parcObject_AcquireRelease); +} + +LONGBOW_TEST_FIXTURE_SETUP(Performance) +{ + parcMemory_SetInterface(&PARCStdlibMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Performance) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Performance, _parcObject_PrefixLength_10000000) +{ + // Test that the result is a multiple of the alignment value and greater than the size of _PARCObjectHeader. + + // Compute the power of 2 value of sizeof(void *) + unsigned int v = sizeof(void *); + unsigned int r = 0; // r will be lg(v) + + while (v >>= 1) { + r++; + } + + PARCObjectDescriptor descriptor; + + for (int i = r; i < 20; i++) { + descriptor.objectAlignment = 1 << i; + size_t actual = _parcObject_PrefixLength(&descriptor); + assertTrue((actual & (descriptor.objectAlignment - 1)) == 0, + "Alignment needs to be a multiple of %u", descriptor.objectAlignment); + } +} + +#define OBJECT_COUNT 10000000 +#define OBJECT_SIZE 1200 + +typedef struct { char bytes[OBJECT_SIZE]; } PerformanceObject; + +parcObject_Override(PerformanceObject, PARCObject); + +LONGBOW_TEST_CASE(Performance, parcObject_CreateRelease) +{ + for (int i = 0; i < OBJECT_COUNT; i++) { + PARCObject *object = parcObject_CreateInstanceImpl(&PerformanceObject_Descriptor); + + PARCObject *object1 = parcObject_Acquire(object); + PARCObject *object2 = parcObject_Acquire(object); + parcObject_Release(&object1); + parcObject_Release(&object2); + + parcObject_Release(&object); + } +} + +void *objects[OBJECT_COUNT]; + +LONGBOW_TEST_CASE(Performance, parcObject_AcquireRelease) +{ + PARCObject *object = parcObject_CreateInstanceImpl(&PerformanceObject_Descriptor); + + for (int i = 0; i < OBJECT_COUNT; i++) { + objects[i] = parcObject_Acquire(object); + } + + for (int i = 0; i < OBJECT_COUNT; i++) { + parcObject_Release(&objects[i]); + } + + parcObject_Release(&object); +} + +LONGBOW_TEST_CASE(Performance, parcObject_Create) +{ + for (int i = 0; i < OBJECT_COUNT; i++) { + objects[i] = parcObject_CreateInstanceImpl(&PerformanceObject_Descriptor); + } + + for (int i = 0; i < OBJECT_COUNT; i++) { + parcObject_Release(&objects[i]); + } +} + +LONGBOW_TEST_FIXTURE(Meta) +{ + LONGBOW_RUN_TEST_CASE(Meta, _metaDestructor_True); + LONGBOW_RUN_TEST_CASE(Meta, _metaDestructor_False); + LONGBOW_RUN_TEST_CASE(Meta, _metaDestructor_None); + + LONGBOW_RUN_TEST_CASE(Meta, parcObjectDescriptor_Create); +} + +LONGBOW_TEST_FIXTURE_SETUP(Meta) +{ + _originalMemoryProvider = parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + _DummyObject *data; + parcMemory_MemAlign((void **) &data, sizeof(void *), sizeof(_DummyObject)); + data->val = 10; + longBowTestCase_SetClipBoardData(testCase, data); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Meta) +{ + _DummyObject *data = longBowTestCase_GetClipBoardData(testCase); + parcMemory_Deallocate((void **) &data); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + parcMemory_SetInterface(_originalMemoryProvider); + if (outstandingAllocations != 0) { + printf("%s leaks %d memory allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Meta, parcObjectDescriptor_Create) +{ + const PARCObjectDescriptor *interface = parcObjectDescriptor_Create("Meta", + sizeof(struct timeval), sizeof(void*), + true, + _meta_destructor_true, NULL, _meta_copy, _meta_toString, + _meta_equals, _meta_compare, _meta_hashCode, _meta_toJson, NULL, + &PARCObject_Descriptor, NULL); + + assertNotNull(interface, "Expected interface instance to be allocated correctly."); + + parcObjectDescriptor_Destroy((PARCObjectDescriptor **) &interface); + assertNull(interface, "Expected parcObjectDescriptor_Destroy to NULL the input pointer"); +} + +LONGBOW_TEST_CASE(Meta, _metaDestructor_True) +{ + const PARCObjectDescriptor *interface = + parcObjectDescriptor_Create("Meta", sizeof(struct timeval), sizeof(void*), true, + _meta_destructor_true, NULL, _meta_copy, _meta_toString, _meta_equals, _meta_compare, _meta_hashCode, _meta_toJson, NULL, + &PARCObject_Descriptor, NULL); + _DummyObject *data = longBowTestCase_GetClipBoardData(testCase); + bool actual = _parcObject_Destructor(interface, (PARCObject **) &data); + + assertTrue(actual, "Expected destructor to return true."); + + parcObjectDescriptor_Destroy((PARCObjectDescriptor **) &interface); +} + +LONGBOW_TEST_CASE(Meta, _metaDestructor_False) +{ + const PARCObjectDescriptor *descriptor = + parcObjectDescriptor_Create("Meta", sizeof(struct timeval), sizeof(void*), true, + _meta_destructor_false, NULL, _meta_copy, _meta_toString, _meta_equals, _meta_compare, _meta_hashCode, _meta_toJson, NULL, + &PARCObject_Descriptor, NULL); + _DummyObject *data = longBowTestCase_GetClipBoardData(testCase); + bool actual = _parcObject_Destructor(descriptor, (PARCObject **) &data); + + assertNull(data, "Expected destructor function to have been called to nullify the reference."); + assertFalse(actual, "Expected destructor to return false."); + + parcObjectDescriptor_Destroy((PARCObjectDescriptor **) &descriptor); +} + +LONGBOW_TEST_CASE(Meta, _metaDestructor_None) +{ + const PARCObjectDescriptor *interface = parcObjectDescriptor_Create("Meta", sizeof(struct timeval), sizeof(void*), true, + NULL, NULL, _meta_copy, _meta_toString, _meta_equals, _meta_compare, _meta_hashCode, _meta_toJson, NULL, &PARCObject_Descriptor, NULL); + _DummyObject *data = longBowTestCase_GetClipBoardData(testCase); + _parcObject_Destructor(interface, (void **) &data); + + assertNotNull(data, "Expected destructor function to have been called to nullify the reference."); + + parcObjectDescriptor_Destroy((PARCObjectDescriptor **) &interface); +} + +LONGBOW_TEST_FIXTURE(PARCObjectDescriptor) +{ + LONGBOW_RUN_TEST_CASE(PARCObjectDescriptor, parcObjectDescriptor_Create); + LONGBOW_RUN_TEST_CASE(PARCObjectDescriptor, parcObjectDescriptor_CreateExtension); + LONGBOW_RUN_TEST_CASE(PARCObjectDescriptor, parcObjectDescriptor_GetSuperType); + LONGBOW_RUN_TEST_CASE(PARCObjectDescriptor, parcObjectDescriptor_GetTypeState); +} + +LONGBOW_TEST_FIXTURE_SETUP(PARCObjectDescriptor) +{ + longBowTestCase_SetInt(testCase, "initialAllocations", parcMemory_Outstanding()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(PARCObjectDescriptor) +{ + int initialAllocations = longBowTestCase_GetInt(testCase, "initialAllocations"); + + uint32_t outstandingAllocations = parcMemory_Outstanding() - initialAllocations; + + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestRunner_GetName(testRunner), outstandingAllocations); + parcSafeMemory_ReportAllocation(STDOUT_FILENO); + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(PARCObjectDescriptor, parcObjectDescriptor_Create) +{ + const PARCObjectDescriptor *descriptor = parcObjectDescriptor_Create("Meta", sizeof(struct timeval), sizeof(void*), true, + NULL, NULL, _meta_copy, _meta_toString, _meta_equals, _meta_compare, _meta_hashCode, _meta_toJson, NULL, &PARCObject_Descriptor, NULL); + + parcObjectDescriptor_Destroy((PARCObjectDescriptor **) &descriptor); +} + +LONGBOW_TEST_CASE(PARCObjectDescriptor, parcObjectDescriptor_CreateExtension) +{ + PARCObjectDescriptor *descriptor = parcObjectDescriptor_Create("Meta", sizeof(struct timeval), sizeof(void*), true, + NULL, NULL, _meta_copy, _meta_toString, _meta_equals, _meta_compare, _meta_hashCode, _meta_toJson, NULL, &PARCObject_Descriptor, NULL); + + PARCObjectDescriptor *extension = parcObjectDescriptor_CreateExtension(descriptor, "Extension"); + + parcObjectDescriptor_Destroy(&extension); + parcObjectDescriptor_Destroy(&descriptor); +} + +LONGBOW_TEST_CASE(PARCObjectDescriptor, parcObjectDescriptor_GetSuperType) +{ + PARCObjectDescriptor *descriptor = parcObjectDescriptor_Create("Meta", sizeof(struct timeval), sizeof(void*), true, + NULL, NULL, _meta_copy, _meta_toString, _meta_equals, _meta_compare, _meta_hashCode, _meta_toJson, NULL, &PARCObject_Descriptor, NULL); + + const PARCObjectDescriptor *superType = parcObjectDescriptor_GetSuperType(descriptor); + + assertTrue(superType == &PARCObject_Descriptor, "Expected a pointer to PARCObject_Descriptor"); + + parcObjectDescriptor_Destroy(&descriptor); +} + +LONGBOW_TEST_CASE(PARCObjectDescriptor, parcObjectDescriptor_GetTypeState) +{ + PARCObjectDescriptor *descriptor = parcObjectDescriptor_Create("Meta", sizeof(struct timeval), sizeof(void*), true, + NULL, NULL, _meta_copy, _meta_toString, _meta_equals, + _meta_compare, _meta_hashCode, _meta_toJson, NULL, + &PARCObject_Descriptor, + (PARCObjectTypeState *) &PARCObject_Descriptor); + + PARCObjectTypeState *state = parcObjectDescriptor_GetTypeState(descriptor); + + assertTrue(state == &PARCObject_Descriptor, "Expected a pointer to PARCObject_Descriptor"); + + parcObjectDescriptor_Destroy(&descriptor); +} + +LONGBOW_TEST_FIXTURE(StaticObjects) +{ + LONGBOW_RUN_TEST_CASE(StaticObjects, parcObject_WrapImpl); + LONGBOW_RUN_TEST_CASE(StaticObjects, parcObject_InitInstanceImpl); + LONGBOW_RUN_TEST_CASE(StaticObjects, parcObject_InitAndClearInstanceImpl); +} + +LONGBOW_TEST_FIXTURE_SETUP(StaticObjects) +{ + _originalMemoryProvider = parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(StaticObjects) +{ + parcMemory_SetInterface(_originalMemoryProvider); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(StaticObjects, parcObject_WrapImpl) +{ + char *origin = (char[parcObject_TotalSize(sizeof(void*), 10)]) { 0 }; + + PARCObject *result = parcObject_WrapImpl(origin, &parcObject_DescriptorName(PARCObject)); + + parcObject_AssertValid(result); + + parcObject_Release(&result); +} + +PARCObject *globalObject = parcObject_Instance(PARCObject, sizeof(void*), 10); + +LONGBOW_TEST_CASE(StaticObjects, parcObject_InitInstanceImpl) +{ + parcObject_InitInstanceImpl(globalObject, &PARCObject_Descriptor); + + parcObject_AssertValid(globalObject); + +// parcObject_Release(&globalObject); +} + +LONGBOW_TEST_CASE(StaticObjects, parcObject_InitAndClearInstanceImpl) +{ + parcObject_InitAndClearInstanceImpl(globalObject, &PARCObject_Descriptor); + + parcObject_AssertValid(globalObject); + +// parcObject_Release(&globalObject); +} + +LONGBOW_TEST_FIXTURE(Synchronization) +{ + LONGBOW_RUN_TEST_CASE(Synchronization, parcObject_SynchronizeBegin); +} + +LONGBOW_TEST_FIXTURE_SETUP(Synchronization) +{ + _originalMemoryProvider = parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Synchronization) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + + parcMemory_SetInterface(_originalMemoryProvider); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestRunner_GetName(testRunner), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Synchronization, parcObject_SynchronizeBegin) +{ + PARCObject *dummy = parcObject_CreateInstance(_DummyObject); + + bool result = parcObject_BarrierSet(dummy); + assertTrue(result, "Expected parcObject_BarrierSet to always return true."); + + _PARCObjectHeader *header = _parcObject_Header(dummy); + assertTrue(header->barrier, "Expected the header barrier to be set."); + + result = parcObject_BarrierUnset(dummy); + assertFalse(result, "Expected parcObject_BarrierUnset to always return false."); + assertFalse(header->barrier, "Expected the header barrier to NOT be set."); + + parcObject_Release(&dummy); +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parcObject); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} + diff --git a/libparc/parc/algol/test/test_parc_PathName.c b/libparc/parc/algol/test/test_parc_PathName.c new file mode 100755 index 00000000..b596aaa2 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_PathName.c @@ -0,0 +1,458 @@ +/* + * 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 "../parc_PathName.c" +#include <LongBow/unit-test.h> + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../parc_SafeMemory.h" +#include <parc/testing/parc_ObjectTesting.h> + +#define PATH_SEGMENT "A" + +LONGBOW_TEST_RUNNER(parc_PathName) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Local); + LONGBOW_RUN_TEST_FIXTURE(AcquireRelease); + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_PathName) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_PathName) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(AcquireRelease) +{ + LONGBOW_RUN_TEST_CASE(AcquireRelease, parcPathName_Create); + LONGBOW_RUN_TEST_CASE(AcquireRelease, parcPathName_Release); + LONGBOW_RUN_TEST_CASE(AcquireRelease, parcPathName_AcquireRelease); +} + +LONGBOW_TEST_FIXTURE_SETUP(AcquireRelease) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(AcquireRelease) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(AcquireRelease, parcPathName_Create) +{ + PARCPathName *pathName = parcPathName_Create(); + assertNotNull(pathName, "Expected a non-null pointer"); + + parcPathName_Release(&pathName); + assertNull(pathName, "Expected parcPathName_Release to null the pointer"); +} + +LONGBOW_TEST_CASE(AcquireRelease, parcPathName_Release) +{ + PARCPathName *pathName = parcPathName_Create(); + assertNotNull(pathName, "Expected a non-null pointer"); + + parcPathName_Release(&pathName); + assertNull(pathName, "Expected parcPathName_Release to null the pointer"); +} + +LONGBOW_TEST_CASE(AcquireRelease, parcPathName_AcquireRelease) +{ + PARCPathName *original = parcPathName_Create(); + assertNotNull(original, "Expected non-null result from parcPathName_Create()"); + + PARCPathName *reference = parcPathName_Acquire(original); + assertTrue(original == reference, "Expected the reference to be equal to the original."); + + parcPathName_Release(&original); + assertNull(original, "Expected parcDeque_Release to null the pointer"); + + parcPathName_Append(reference, (void *) "Hello"); + size_t expected = 1; + size_t actual = parcPathName_Size(reference); + assertTrue(expected == actual, + "Expected size %zd, actual %zd", expected, actual); + parcPathName_Release(&reference); +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcPathName_Create); + + LONGBOW_RUN_TEST_CASE(Global, parcPathName_Size); + LONGBOW_RUN_TEST_CASE(Global, parcPathName_Append); + LONGBOW_RUN_TEST_CASE(Global, parcPathName_Prepend); + LONGBOW_RUN_TEST_CASE(Global, parcPathName_IsAbsolute); + LONGBOW_RUN_TEST_CASE(Global, parcPathName_MakeAbsolute); + LONGBOW_RUN_TEST_CASE(Global, parcPathName_Parse_AbsolutePath); + LONGBOW_RUN_TEST_CASE(Global, parcPathName_Parse_AbsolutePath_Limited); + LONGBOW_RUN_TEST_CASE(Global, parcPathName_Parse_RelativePath); + LONGBOW_RUN_TEST_CASE(Global, parcPathName_ToString_AbsolutePath); + LONGBOW_RUN_TEST_CASE(Global, parcPathName_ToString_RelativePath); + LONGBOW_RUN_TEST_CASE(Global, parcPathName_Head); + LONGBOW_RUN_TEST_CASE(Global, parcPathName_Tail); + LONGBOW_RUN_TEST_CASE(Global, parcPathName_Tail_ExceedsLength); + LONGBOW_RUN_TEST_CASE(Global, parcPathName_Equals); + LONGBOW_RUN_TEST_CASE(Global, parcPathName_Copy); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcPathName_Size) +{ + char *path = "/a/b/c"; + PARCPathName *pathName = parcPathName_Parse(path); + assertNotNull(pathName, "Expected a non-null pointer"); + assertTrue(pathName->isAbsolute, "Expected the PARCPathName to be absolute."); + + assertTrue(parcPathName_Size(pathName) == 3, "Expected 3, actual %zu", parcPathName_Size(pathName)); + parcPathName_Release(&pathName); +} + +LONGBOW_TEST_CASE(Global, parcPathName_Prepend) +{ + PARCPathName *pathName = parcPathName_Create(); + size_t size = 1000; + + char expected[10]; + for (size_t i = 0; i < size; i++) { + sprintf(expected, "%zd", i); + parcPathName_Prepend(pathName, expected); + } + assertNotNull(pathName, "Expected a non-null pointer"); + + size_t actual = parcPathName_Size(pathName); + assertTrue(size == actual, + "Expected %zd, actual %zd", size, actual); + + for (size_t i = 0; i < size; i++) { + sprintf(expected, "%zd", size - i - 1); + char *segment = parcDeque_GetAtIndex(pathName->path, i); + assertTrue(strcmp(segment, expected) == 0, + "Expected %s, actual %s", expected, segment); + } + + parcPathName_Release(&pathName); +} + +LONGBOW_TEST_CASE(Global, parcPathName_Append) +{ + PARCPathName *pathName = parcPathName_Create(); + size_t size = 1000; + + char expected[10]; + for (size_t i = 0; i < size; i++) { + sprintf(expected, "%zd", i); + parcPathName_Append(pathName, expected); + } + assertNotNull(pathName, "Expected a non-null pointer"); + + size_t actual = parcPathName_Size(pathName); + assertTrue(size == actual, + "Expected %zd, actual %zd", size, actual); + + for (size_t i = 0; i < size; i++) { + sprintf(expected, "%zd", i); + char *segment = parcDeque_GetAtIndex(pathName->path, i); + assertTrue(strcmp(segment, expected) == 0, + "Expected %s, actual %s", expected, segment); + } + + parcPathName_Release(&pathName); +} + +LONGBOW_TEST_CASE(Global, parcPathName_Create) +{ + PARCPathName *pathName = parcPathName_Create(); + assertNotNull(pathName, "Expected a non-null pointer"); + + parcPathName_Release(&pathName); + assertNull(pathName, "Expected parcPathName_Release to null the pointer"); +} + +LONGBOW_TEST_CASE(Global, parcPathName_IsAbsolute) +{ + char *path = "/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/aa/bb/cc/dd/ee/ff/gg/hh/ii/jj/kk/ll/mm/nn/oo/pp/qq/rr/ss/tt/uu/vv/ww/xx/yy/zz"; + PARCPathName *pathName = parcPathName_Parse(path); + assertNotNull(pathName, "Expected a non-null pointer"); + assertTrue(parcPathName_IsAbsolute(pathName), "Expected the PARCPathName to be absolute."); + + parcPathName_Release(&pathName); +} + +LONGBOW_TEST_CASE(Global, parcPathName_MakeAbsolute) +{ +#define PATH "a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z" + char *expected = PATH; + PARCPathName *pathName = parcPathName_Parse(expected); + assertNotNull(pathName, "Expected a non-null pointer"); + assertFalse(pathName->isAbsolute, "Expected the PARCPathName to be relative."); + + parcPathName_MakeAbsolute(pathName, true); + + char *actual = parcPathName_ToString(pathName); + assertTrue(strcmp("/" PATH, actual) == 0, + "Expected '%s' actual '%s'", expected, actual); + parcMemory_Deallocate((void **) &actual); + parcPathName_Release(&pathName); +} + +LONGBOW_TEST_CASE(Global, parcPathName_Parse_AbsolutePath) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + for (int i = 0; i < 1000; i++) { + parcBufferComposer_Format(composer, "/%d", i); + } + + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + char *path = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + + PARCPathName *pathName = parcPathName_Parse(path); + assertNotNull(pathName, "Expected a non-null pointer"); + assertTrue(pathName->isAbsolute, "Expected the PARCPathName to be absolute."); + + + char *actual = parcPathName_ToString(pathName); + assertTrue(strcmp(path, actual) == 0, "Expected %s, actual %s", path, actual); + + parcMemory_Deallocate((void **) &actual); + parcMemory_Deallocate((void **) &path); + + parcPathName_Release(&pathName); + parcBufferComposer_Release(&composer); +} + +LONGBOW_TEST_CASE(Global, parcPathName_Parse_AbsolutePath_Limited) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + for (int i = 0; i < 10; i++) { + parcBufferComposer_Format(composer, "/%d", i); + } + parcBufferComposer_Format(composer, "?hello world"); + + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + char *path = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + + size_t limit = strchr(path, '?') - path; + PARCPathName *pathName = parcPathName_ParseToLimit(limit, path); + + assertNotNull(pathName, "Expected a non-null pointer"); + assertTrue(pathName->isAbsolute, "Expected the PARCPathName to be absolute."); + + path[limit] = 0; + char *actual = parcPathName_ToString(pathName); + assertTrue(strcmp(path, actual) == 0, "Expected %s, actual %s", path, actual); + + parcMemory_Deallocate((void **) &actual); + parcMemory_Deallocate((void **) &path); + + parcPathName_Release(&pathName); + parcBufferComposer_Release(&composer); +} + +LONGBOW_TEST_CASE(Global, parcPathName_Parse_RelativePath) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + for (int i = 0; i < 1000; i++) { + parcBufferComposer_Format(composer, "%d/", i); + } + parcBufferComposer_Format(composer, "%d", 1000); + + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + char *expected = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + + PARCPathName *pathName = parcPathName_Parse(expected); + assertNotNull(pathName, "Expected a non-null pointer"); + assertFalse(pathName->isAbsolute, "Expected the PARCPathName to be relative."); + + char *actual = parcPathName_ToString(pathName); + assertTrue(strcmp(expected, actual) == 0, + "Expected '%s' actual '%s'", expected, actual); + parcMemory_Deallocate((void **) &actual); + parcMemory_Deallocate((void **) &expected); + + parcPathName_Release(&pathName); + + parcBufferComposer_Release(&composer); +} + +LONGBOW_TEST_CASE(Global, parcPathName_ToString_AbsolutePath) +{ + char *path = "/a/b/c"; + PARCPathName *pathName = parcPathName_Parse(path); + assertNotNull(pathName, "Expected a non-null pointer"); + assertTrue(pathName->isAbsolute, "Expected the PARCPathName to be absolute."); + + char *actual = parcPathName_ToString(pathName); + + assertTrue(strcmp(path, actual) == 0, "Expected '%s' actual '%s'", path, actual); + + parcMemory_Deallocate((void **) &actual); + + parcPathName_Release(&pathName); +} + +LONGBOW_TEST_CASE(Global, parcPathName_ToString_RelativePath) +{ + char *path = "a/b/c"; + PARCPathName *pathName = parcPathName_Parse(path); + assertNotNull(pathName, "Expected a non-null pointer"); + assertFalse(pathName->isAbsolute, "Expected the PARCPathName to be relative."); + + char *actual = parcPathName_ToString(pathName); + + assertTrue(strcmp(path, actual) == 0, "Expected '%s' actual '%s'", path, actual); + + parcMemory_Deallocate((void **) &actual); + + parcPathName_Release(&pathName); +} + +LONGBOW_TEST_CASE(Global, parcPathName_Head) +{ + PARCPathName *original = parcPathName_Parse("/" PATH_SEGMENT "/" PATH_SEGMENT "/" PATH_SEGMENT "/" PATH_SEGMENT); + assertNotNull(original, "Expected a non-null pointer"); + + PARCPathName *expected = parcPathName_Parse("/" PATH_SEGMENT "/" PATH_SEGMENT "/" PATH_SEGMENT); + PARCPathName *actual = parcPathName_Head(original, 3); + + assertTrue(parcPathName_Equals(expected, actual), + "expected did not match actual"); + + parcPathName_Release(&original); + parcPathName_Release(&expected); + parcPathName_Release(&actual); +} + +LONGBOW_TEST_CASE(Global, parcPathName_Tail) +{ + PARCPathName *original = parcPathName_Parse("/" PATH_SEGMENT "/" PATH_SEGMENT "/" PATH_SEGMENT "/" PATH_SEGMENT "/" PATH_SEGMENT "/" PATH_SEGMENT); + assertNotNull(original, "Expected a non-null pointer"); + + PARCPathName *expected = parcPathName_Parse(PATH_SEGMENT "/" PATH_SEGMENT "/" PATH_SEGMENT); + PARCPathName *actual = parcPathName_Tail(original, 3); + + assertTrue(parcPathName_Equals(expected, actual), + "expected did not match actual"); + + parcPathName_Release(&original); + parcPathName_Release(&expected); + parcPathName_Release(&actual); +} + +LONGBOW_TEST_CASE(Global, parcPathName_Tail_ExceedsLength) +{ + PARCPathName *original = parcPathName_Parse("/" PATH_SEGMENT "/" PATH_SEGMENT "/" PATH_SEGMENT "/" PATH_SEGMENT "/" PATH_SEGMENT "/" PATH_SEGMENT); + assertNotNull(original, "Expected a non-null pointer"); + + PARCPathName *actual = parcPathName_Tail(original, 10000000); + + parcPathName_MakeAbsolute(original, false); + + assertTrue(parcPathName_Equals(original, actual), + "expected did not match actual"); + + parcPathName_Release(&original); + parcPathName_Release(&actual); +} + +LONGBOW_TEST_CASE(Global, parcPathName_Equals) +{ + PARCPathName *x = parcPathName_Parse("/a/b/c/d/"); + PARCPathName *y = parcPathName_Parse("/a/b/c/d/"); + PARCPathName *z = parcPathName_Parse("/a/b/c/d/"); + PARCPathName *u1 = parcPathName_Parse("/a/b/c/d/e"); + PARCPathName *u2 = parcPathName_Parse("/a/b/c/"); + PARCPathName *u3 = parcPathName_Parse("a/b/c/"); + + parcObjectTesting_AssertEqualsFunction(parcPathName_Equals, x, y, z, u1, u2, u3, NULL); + + parcPathName_Release(&x); + parcPathName_Release(&y); + parcPathName_Release(&z); + parcPathName_Release(&u1); + parcPathName_Release(&u2); + parcPathName_Release(&u3); +} + +LONGBOW_TEST_CASE(Global, parcPathName_Copy) +{ + PARCPathName *x = parcPathName_Parse("/a/b/c/d/"); + PARCPathName *y = parcPathName_Copy(x); + + assertTrue(parcPathName_Equals(x, y), "Expected the copy to be equal to the original."); + + parcPathName_Release(&x); + parcPathName_Release(&y); +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_PathName); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_PriorityQueue.c b/libparc/parc/algol/test/test_parc_PriorityQueue.c new file mode 100644 index 00000000..d42066e4 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_PriorityQueue.c @@ -0,0 +1,492 @@ +/* + * 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 the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include <config.h> +#include <inttypes.h> + +#include "../parc_PriorityQueue.c" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +LONGBOW_TEST_RUNNER(parc_PriorityQueue) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_PriorityQueue) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_PriorityQueue) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcPriorityQueue_Add); + LONGBOW_RUN_TEST_CASE(Global, parcPriorityQueue_Add_Expand); + LONGBOW_RUN_TEST_CASE(Global, parcPriorityQueue_Clear); + LONGBOW_RUN_TEST_CASE(Global, parcPriorityQueue_Clear_Destroy); + LONGBOW_RUN_TEST_CASE(Global, parcPriorityQueue_Create); + LONGBOW_RUN_TEST_CASE(Global, parcPriorityQueue_ParcFreeDestroyer); + LONGBOW_RUN_TEST_CASE(Global, parcPriorityQueue_Peek); + LONGBOW_RUN_TEST_CASE(Global, parcPriorityQueue_Poll); + LONGBOW_RUN_TEST_CASE(Global, parcPriorityQueue_Peek_Empty); + LONGBOW_RUN_TEST_CASE(Global, parcPriorityQueue_Poll_Empty); + LONGBOW_RUN_TEST_CASE(Global, parcPriorityQueue_Size); + LONGBOW_RUN_TEST_CASE(Global, parcPriorityQueue_Uint64CompareTo); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + printf("('%s' leaks memory by %d (allocs - frees)) ", longBowTestCase_GetName(testCase), parcMemory_Outstanding()); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcPriorityQueue_Add) +{ + PARCPriorityQueue *queue = parcPriorityQueue_Create(parcPriorityQueue_Uint64CompareTo, NULL); + uint64_t data[] = { 60, 70, 50, 71, 72, 55 }; + size_t count = 6; + + for (int i = 0; i < count; i++) { + parcPriorityQueue_Add(queue, &data[i]); + } + + assertTrue(parcPriorityQueue_Size(queue) == count, "Wrong size got %zu expected %zu", parcPriorityQueue_Size(queue), count); + parcPriorityQueue_Destroy(&queue); +} + +LONGBOW_TEST_CASE(Global, parcPriorityQueue_Add_Expand) +{ + PARCPriorityQueue *queue = parcPriorityQueue_Create(parcPriorityQueue_Uint64CompareTo, NULL); + size_t capacity = queue->capacity; + for (int i = 0; i <= capacity; i++) { + parcPriorityQueue_Add(queue, &capacity); + } + + assertTrue(capacity < queue->capacity, "Did not expand queue before %zu after %zu", capacity, queue->capacity); + parcPriorityQueue_Destroy(&queue); +} + + +LONGBOW_TEST_CASE(Global, parcPriorityQueue_Clear) +{ + PARCPriorityQueue *queue = parcPriorityQueue_Create(parcPriorityQueue_Uint64CompareTo, NULL); + uint64_t data[] = { 60, 70, 50, 71, 72, 55 }; + size_t count = 6; + + for (int i = 0; i < count; i++) { + parcPriorityQueue_Add(queue, &data[i]); + } + + parcPriorityQueue_Clear(queue); + + assertTrue(parcPriorityQueue_Size(queue) == 0, "Wrong size got %zu expected %d", parcPriorityQueue_Size(queue), 0); + parcPriorityQueue_Destroy(&queue); +} + +LONGBOW_TEST_CASE(Global, parcPriorityQueue_Clear_Destroy) +{ + PARCPriorityQueue *queue = parcPriorityQueue_Create(parcPriorityQueue_Uint64CompareTo, parcPriorityQueue_ParcFreeDestroyer); + uint64_t *value = parcMemory_Allocate(sizeof(uint64_t)); + assertNotNull(value, "parcMemory_Allocate(%zu) returned NULL", sizeof(uint64_t)); + *value = 1; + parcPriorityQueue_Add(queue, value); + + parcPriorityQueue_Clear(queue); + + assertTrue(parcPriorityQueue_Size(queue) == 0, "Wrong size got %zu expected %d", parcPriorityQueue_Size(queue), 0); + parcPriorityQueue_Destroy(&queue); + + assertTrue(parcMemory_Outstanding() == 0, "Memory imbalance after clear with destroy: %u", parcMemory_Outstanding()); +} + +LONGBOW_TEST_CASE(Global, parcPriorityQueue_Create) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_CASE(Global, parcPriorityQueue_ParcFreeDestroyer) +{ + size_t before_balance = parcMemory_Outstanding(); + uint64_t *a = parcMemory_Allocate(sizeof(uint64_t)); + assertNotNull(a, "parcMemory_Allocate(%zu) returned NULL", sizeof(uint64_t)); + *a = 1; + parcPriorityQueue_ParcFreeDestroyer((void **) &a); + size_t after_balance = parcMemory_Outstanding(); + assertTrue(a == NULL, "Did not null double pointer"); + assertTrue(before_balance == after_balance, "Memory imbalance after destroy: before %zu after %zu", before_balance, after_balance); +} + +LONGBOW_TEST_CASE(Global, parcPriorityQueue_Peek) +{ + PARCPriorityQueue *queue = parcPriorityQueue_Create(parcPriorityQueue_Uint64CompareTo, NULL); + uint64_t data[] = { 60, 70, 50, 71, 72, 55 }; + size_t count = 6; + + for (int i = 0; i < count; i++) { + parcPriorityQueue_Add(queue, &data[i]); + } + + uint64_t *test = parcPriorityQueue_Peek(queue); + + assertTrue(*test == 50, "Wrong head element, expected 50 got %" PRIu64 "", *test); + assertTrue(parcPriorityQueue_Size(queue) == count, "Queue should not have shunk, size %zu expected %zu", parcPriorityQueue_Size(queue), count); + parcPriorityQueue_Destroy(&queue); +} + +LONGBOW_TEST_CASE(Global, parcPriorityQueue_Poll) +{ + PARCPriorityQueue *queue = parcPriorityQueue_Create(parcPriorityQueue_Uint64CompareTo, NULL); + uint64_t data[] = { 60, 70, 50, 71, 72, 55 }; + size_t count = 6; + + for (int i = 0; i < count; i++) { + parcPriorityQueue_Add(queue, &data[i]); + } + + uint64_t *test = parcPriorityQueue_Poll(queue); + + assertTrue(*test == 50, "Wrong head element, expected 50 got %" PRIu64 "", *test); + assertTrue(queue->size == count - 1, "Queue should have shunk, size %zu expected %zu", queue->size, count - 1); + parcPriorityQueue_Destroy(&queue); +} + +LONGBOW_TEST_CASE(Global, parcPriorityQueue_Peek_Empty) +{ + PARCPriorityQueue *queue = parcPriorityQueue_Create(parcPriorityQueue_Uint64CompareTo, NULL); + uint64_t *test = parcPriorityQueue_Peek(queue); + assertNull(test, "Peek on empty queue should return null, got %p", (void *) test); + parcPriorityQueue_Destroy(&queue); +} + +LONGBOW_TEST_CASE(Global, parcPriorityQueue_Poll_Empty) +{ + PARCPriorityQueue *queue = parcPriorityQueue_Create(parcPriorityQueue_Uint64CompareTo, NULL); + uint64_t *test = parcPriorityQueue_Poll(queue); + assertNull(test, "Poll on empty queue should return null, got %p", (void *) test); + parcPriorityQueue_Destroy(&queue); +} + +LONGBOW_TEST_CASE(Global, parcPriorityQueue_Size) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_CASE(Global, parcPriorityQueue_Uint64CompareTo) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_FIXTURE(Local) +{ + LONGBOW_RUN_TEST_CASE(Local, parcPriorityQueue_BubbleUp_True); + LONGBOW_RUN_TEST_CASE(Local, parcPriorityQueue_BubbleUp_False); + LONGBOW_RUN_TEST_CASE(Local, parcPriorityQueue_Expand); + LONGBOW_RUN_TEST_CASE(Local, parcPriorityQueue_LeftChildIndex); + LONGBOW_RUN_TEST_CASE(Local, parcPriorityQueue_ParentIndex); + LONGBOW_RUN_TEST_CASE(Local, parcPriorityQueue_RightChildIndex); + LONGBOW_RUN_TEST_CASE(Local, parcPriorityQueue_Swap); + LONGBOW_RUN_TEST_CASE(Local, parcPriorityQueue_TrickleDown); + LONGBOW_RUN_TEST_CASE(Local, parcPriorityQueue_TrickleLeftChild_True); + LONGBOW_RUN_TEST_CASE(Local, parcPriorityQueue_TrickleLeftChild_False); + LONGBOW_RUN_TEST_CASE(Local, parcPriorityQueue_TrickleRightChild_Case1_True); + LONGBOW_RUN_TEST_CASE(Local, parcPriorityQueue_TrickleRightChild_Case2_True); + LONGBOW_RUN_TEST_CASE(Local, parcPriorityQueue_TrickleRightChild_Case1_False); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + printf("('%s' leaks memory by %d (allocs - frees)) ", longBowTestCase_GetName(testCase), parcMemory_Outstanding()); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Local, parcPriorityQueue_BubbleUp_True) +{ + PARCPriorityQueue *queue = parcPriorityQueue_Create(parcPriorityQueue_Uint64CompareTo, NULL); + uint64_t data[] = { 50, 6 }; + + queue->array[0].data = &data[0]; + queue->array[1].data = &data[1]; + queue->size = 2; + + _bubbleUp(queue, 1); + assertTrue(queue->array[0].data == &data[1], "Element 6 did not make it to the root"); + + parcPriorityQueue_Destroy(&queue); +} + +LONGBOW_TEST_CASE(Local, parcPriorityQueue_BubbleUp_False) +{ + PARCPriorityQueue *queue = parcPriorityQueue_Create(parcPriorityQueue_Uint64CompareTo, NULL); + uint64_t data[] = { 50, 60 }; + + queue->array[0].data = &data[0]; + queue->array[1].data = &data[1]; + queue->size = 2; + + _bubbleUp(queue, 1); + assertTrue(queue->array[0].data == &data[0], "Element 60 did not stay as child"); + + parcPriorityQueue_Destroy(&queue); +} + + +LONGBOW_TEST_CASE(Local, parcPriorityQueue_Expand) +{ + PARCPriorityQueue *queue = parcPriorityQueue_Create(parcPriorityQueue_Uint64CompareTo, NULL); + size_t before_capacity = queue->capacity; + _expand(queue); + size_t after_capacity = queue->capacity; + + assertTrue(before_capacity < after_capacity, "Expected after capacity %zu to be larger than before %zu", after_capacity, before_capacity); + parcPriorityQueue_Destroy(&queue); +} + +LONGBOW_TEST_CASE(Local, parcPriorityQueue_LeftChildIndex) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_CASE(Local, parcPriorityQueue_ParentIndex) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_CASE(Local, parcPriorityQueue_RightChildIndex) +{ + testUnimplemented(""); +} + +/** + * Swaps two elements + */ +LONGBOW_TEST_CASE(Local, parcPriorityQueue_Swap) +{ + PARCPriorityQueue *queue = parcPriorityQueue_Create(parcPriorityQueue_Uint64CompareTo, NULL); + uint64_t data[] = { 50, 6 }; + + queue->array[0].data = &data[0]; + queue->array[1].data = &data[1]; + queue->size = 2; + + _swap(queue, 0, 1); + assertTrue(queue->array[0].data == &data[1], "array[0] does not equal data[1]: %p != %p", + (void *) queue->array[0].data, (void *) &data[1]); + assertTrue(queue->array[1].data == &data[0], "array[1] does not equal data[0]: %p != %p", + (void *) queue->array[1].data, (void *) &data[0]); + + parcPriorityQueue_Destroy(&queue); +} + +/** + * Tests each case in TrickleDown: + * - right child exists, then + * - no right child, only left child, then + * - no child + * + * 60 50 + * / \ / \ + * 70 50 ====> 70 55 + * / \ / \ / \ / \ + * 71 72 55 x 71 72 60 x + */ +LONGBOW_TEST_CASE(Local, parcPriorityQueue_TrickleDown) +{ + PARCPriorityQueue *queue = parcPriorityQueue_Create(parcPriorityQueue_Uint64CompareTo, NULL); + uint64_t data[] = { 60, 70, 50, 71, 72, 55 }; + + queue->size = 6; + for (int i = 0; i < queue->size; i++) { + queue->array[i].data = &data[i]; + } + + _trickleDown(queue, 0); + assertTrue(*((uint64_t *) queue->array[0].data) == 50, + "Root not 50, got %" PRIu64 "\n", + (uint64_t) *((uint64_t *) queue->array[0].data)); + assertTrue(*((uint64_t *) queue->array[2].data) == 55, + "Right not 55, got %" PRIu64 "\n", + (uint64_t) *((uint64_t *) queue->array[2].data)); + assertTrue(*((uint64_t *) queue->array[5].data) == 60, + "Last not 60, got %" PRIu64 "\n", + (uint64_t) *((uint64_t *) queue->array[5].data)); + + parcPriorityQueue_Destroy(&queue); +} + +/** + * Tests the TRUE case of this condition + * + * Case 3: Left child exists (right does not) and l.value < n.value + * In this case, swap(n.index, l.index) and set n.index = l.index + * 50 6 + * / \ ===> / \ + * 6 x 50 x + */ +LONGBOW_TEST_CASE(Local, parcPriorityQueue_TrickleLeftChild_True) +{ + PARCPriorityQueue *queue = parcPriorityQueue_Create(parcPriorityQueue_Uint64CompareTo, NULL); + uint64_t data[] = { 50, 6 }; + + queue->array[0].data = &data[0]; + queue->array[1].data = &data[1]; + queue->size = 2; + + size_t nextElementIndex = _trickleLeftChild(queue, 0, 1); + assertTrue(nextElementIndex == 1, "nextElementIndex should have been left child 1, got %zu\n", nextElementIndex); + + parcPriorityQueue_Destroy(&queue); +} + +/** + * Tests the FALSE case of this condition + * + * Case 3: Left child exists (right does not) and l.value < n.value + * In this case, swap(n.index, l.index) and set n.index = l.index + * 50 6 + * / \ ===> / \ + * 6 x 50 x + */ +LONGBOW_TEST_CASE(Local, parcPriorityQueue_TrickleLeftChild_False) +{ + PARCPriorityQueue *queue = parcPriorityQueue_Create(parcPriorityQueue_Uint64CompareTo, NULL); + uint64_t data[] = { 6, 50 }; + + queue->array[0].data = &data[0]; + queue->array[1].data = &data[1]; + queue->size = 2; + + size_t nextElementIndex = _trickleLeftChild(queue, 0, 1); + assertTrue(nextElementIndex == 0, "nextElementIndex should have been root 0, got %zu\n", nextElementIndex); + + parcPriorityQueue_Destroy(&queue); +} + + +/** + * Tests the TRUE case + * + * Case 1: Right child exists and r.value < n.value && r.value < l.value + * In this case, swap(n.index, r.index) and set n.index = r.index. + * 50 6 + * / \ ===> / \ + * 9 6 9 50 + */ +LONGBOW_TEST_CASE(Local, parcPriorityQueue_TrickleRightChild_Case1_True) +{ + PARCPriorityQueue *queue = parcPriorityQueue_Create(parcPriorityQueue_Uint64CompareTo, NULL); + uint64_t data[] = { 50, 9, 6 }; + + queue->array[0].data = &data[0]; + queue->array[1].data = &data[1]; + queue->array[2].data = &data[2]; + queue->size = 3; + + size_t nextElementIndex = _trickleRightChild(queue, 0, 1, 2); + assertTrue(nextElementIndex == 2, "nextElementIndex should have been right 2, got %zu\n", nextElementIndex); + + parcPriorityQueue_Destroy(&queue); +} + +/** + * Tests the FALSE case + * + * Case 1: Right child exists and r.value < n.value && r.value < l.value + * In this case, swap(n.index, r.index) and set n.index = r.index. + * 50 6 + * / \ ===> / \ + * 9 6 9 50 + */ +LONGBOW_TEST_CASE(Local, parcPriorityQueue_TrickleRightChild_Case1_False) +{ + PARCPriorityQueue *queue = parcPriorityQueue_Create(parcPriorityQueue_Uint64CompareTo, NULL); + + // r.value not < n.value + uint64_t data[] = { 6, 9, 50 }; + + queue->array[0].data = &data[0]; + queue->array[1].data = &data[1]; + queue->array[2].data = &data[2]; + queue->size = 3; + + size_t nextElementIndex = _trickleRightChild(queue, 0, 1, 2); + assertTrue(nextElementIndex == 0, "nextElementIndex should have been root 0, got %zu\n", nextElementIndex); + + parcPriorityQueue_Destroy(&queue); +} + +/** + * Tests the TRUE case + * + * Case 2: Right child exists and r.value < n.value && l.value <= r.value + * In this case swap(n.index, l.index) and set n.index = l.index + * This makes sense by transitivity that l <= r < n, so swap(n,l) satisfies the invariant. + * 50 6 + * / \ ===> / \ + * 6 9 50 9 + */ +LONGBOW_TEST_CASE(Local, parcPriorityQueue_TrickleRightChild_Case2_True) +{ + PARCPriorityQueue *queue = parcPriorityQueue_Create(parcPriorityQueue_Uint64CompareTo, NULL); + uint64_t data[] = { 50, 6, 9 }; + + queue->array[0].data = &data[0]; + queue->array[1].data = &data[1]; + queue->array[2].data = &data[2]; + queue->size = 3; + + size_t nextElementIndex = _trickleRightChild(queue, 0, 1, 2); + assertTrue(nextElementIndex == 1, "nextElementIndex should have been left 1, got %zu\n", nextElementIndex); + + parcPriorityQueue_Destroy(&queue); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_PriorityQueue); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_Properties.c b/libparc/parc/algol/test/test_parc_Properties.c new file mode 100644 index 00000000..994b8d8e --- /dev/null +++ b/libparc/parc/algol/test/test_parc_Properties.c @@ -0,0 +1,277 @@ +/* + * 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 "../parc_Properties.c" + +#include <LongBow/testing.h> +#include <LongBow/debugging.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_SafeMemory.h> +#include <parc/algol/parc_DisplayIndented.h> + +#include <parc/testing/parc_MemoryTesting.h> +#include <parc/testing/parc_ObjectTesting.h> + +LONGBOW_TEST_RUNNER(parc_Properties) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(CreateAcquireRelease); + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Specialized); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_Properties) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_Properties) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(CreateAcquireRelease) +{ + LONGBOW_RUN_TEST_CASE(CreateAcquireRelease, CreateRelease); +} + +LONGBOW_TEST_FIXTURE_SETUP(CreateAcquireRelease) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(CreateAcquireRelease) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(CreateAcquireRelease, CreateRelease) +{ + PARCProperties *instance = parcProperties_Create(); + assertNotNull(instance, "Expected non-null result from parcProperties_Create();"); + + parcObjectTesting_AssertAcquire(instance); + + parcProperties_Release(&instance); + assertNull(instance, "Expected null result from parcProperties_Release();"); +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcProperties_Compare); + LONGBOW_RUN_TEST_CASE(Global, parcProperties_Copy); + LONGBOW_RUN_TEST_CASE(Global, parcProperties_Display); + LONGBOW_RUN_TEST_CASE(Global, parcProperties_Equals); + LONGBOW_RUN_TEST_CASE(Global, parcProperties_HashCode); + LONGBOW_RUN_TEST_CASE(Global, parcProperties_IsValid); + LONGBOW_RUN_TEST_CASE(Global, parcProperties_ToJSON); + LONGBOW_RUN_TEST_CASE(Global, parcProperties_ToString); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s mismanaged memory.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcProperties_Compare) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_CASE(Global, parcProperties_Copy) +{ + PARCProperties *instance = parcProperties_Create(); + PARCProperties *copy = parcProperties_Copy(instance); + assertTrue(parcProperties_Equals(instance, copy), "Expected the copy to be equal to the original"); + + parcProperties_Release(&instance); + parcProperties_Release(©); +} + +LONGBOW_TEST_CASE(Global, parcProperties_Display) +{ + PARCProperties *instance = parcProperties_Create(); + parcProperties_SetProperty(instance, "foo", "bar"); + parcProperties_SetProperty(instance, "xyzzy", "plugh"); + + parcProperties_Display(instance, 0); + parcProperties_Release(&instance); +} + +LONGBOW_TEST_CASE(Global, parcProperties_Equals) +{ + PARCProperties *x = parcProperties_Create(); + PARCProperties *y = parcProperties_Create(); + PARCProperties *z = parcProperties_Create(); + + parcObjectTesting_AssertEquals(x, y, z, NULL); + + parcProperties_Release(&x); + parcProperties_Release(&y); + parcProperties_Release(&z); +} + +LONGBOW_TEST_CASE(Global, parcProperties_HashCode) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_CASE(Global, parcProperties_IsValid) +{ + PARCProperties *instance = parcProperties_Create(); + assertTrue(parcProperties_IsValid(instance), "Expected parcProperties_Create to result in a valid instance."); + + parcProperties_Release(&instance); + assertFalse(parcProperties_IsValid(instance), "Expected parcProperties_Release to result in an invalid instance."); +} + +LONGBOW_TEST_CASE(Global, parcProperties_ToJSON) +{ + PARCProperties *instance = parcProperties_Create(); + + parcProperties_SetProperty(instance, "foo", "bar"); + PARCJSON *json = parcProperties_ToJSON(instance); + + parcJSON_Release(&json); + + parcProperties_Release(&instance); +} + +LONGBOW_TEST_CASE(Global, parcProperties_ToString) +{ + PARCProperties *instance = parcProperties_Create(); + + parcProperties_SetProperty(instance, "foo", "bar"); + parcProperties_SetProperty(instance, "bar", "baz"); + char *string = parcProperties_ToString(instance); + + assertNotNull(string, "Expected non-NULL result from parcProperties_ToString"); + + parcMemory_Deallocate((void **) &string); + parcProperties_Release(&instance); +} + +LONGBOW_TEST_FIXTURE(Specialized) +{ + LONGBOW_RUN_TEST_CASE(Specialized, parcProperties_SetProperty); + LONGBOW_RUN_TEST_CASE(Specialized, parcProperties_GetProperty); + LONGBOW_RUN_TEST_CASE(Specialized, parcProperties_GetPropertyDefault); + LONGBOW_RUN_TEST_CASE(Specialized, parcProperties_GetAsBoolean_true); + LONGBOW_RUN_TEST_CASE(Specialized, parcProperties_GetAsBoolean_false); +} + +LONGBOW_TEST_FIXTURE_SETUP(Specialized) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Specialized) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s mismanaged memory.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Specialized, parcProperties_SetProperty) +{ + PARCProperties *instance = parcProperties_Create(); + char *expected = "bar"; + parcProperties_SetProperty(instance, "foo", expected); + + const char *actual = parcProperties_GetProperty(instance, "foo"); + assertTrue(strcmp("bar", actual) == 0, "Expected %s, actual %s", expected, actual); + + parcProperties_Release(&instance); +} + +LONGBOW_TEST_CASE(Specialized, parcProperties_GetProperty) +{ + PARCProperties *instance = parcProperties_Create(); + char *expected = "bar"; + parcProperties_SetProperty(instance, "foo", expected); + + const char *actual = parcProperties_GetProperty(instance, "foo"); + assertTrue(strcmp("bar", actual) == 0, "Expected %s, actual %s", expected, actual); + + parcProperties_Release(&instance); +} + +LONGBOW_TEST_CASE(Specialized, parcProperties_GetPropertyDefault) +{ + PARCProperties *instance = parcProperties_Create(); + char *expected = "bar"; + parcProperties_SetProperty(instance, "foo", expected); + + const char *actual = parcProperties_GetPropertyDefault(instance, "blurfl", "defaultValue"); + assertTrue(strcmp("defaultValue", actual) == 0, "Expected %s, actual %s", "defaultValue", actual); + + parcProperties_Release(&instance); +} + +LONGBOW_TEST_CASE(Specialized, parcProperties_GetAsBoolean_true) +{ + PARCProperties *instance = parcProperties_Create(); + char *expected = "true"; + parcProperties_SetProperty(instance, "foo", expected); + + bool actual = parcProperties_GetAsBoolean(instance, "foo", false); + assertTrue(actual, "Expected true"); + + parcProperties_Release(&instance); +} + +LONGBOW_TEST_CASE(Specialized, parcProperties_GetAsBoolean_false) +{ + PARCProperties *instance = parcProperties_Create(); + char *expected = "false"; + parcProperties_SetProperty(instance, "foo", expected); + + bool actual = parcProperties_GetAsBoolean(instance, "foo", true); + assertFalse(actual, "Expected false"); + + parcProperties_Release(&instance); +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_Properties); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} + + diff --git a/libparc/parc/algol/test/test_parc_RandomAccessFile.c b/libparc/parc/algol/test/test_parc_RandomAccessFile.c new file mode 100644 index 00000000..796b69d8 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_RandomAccessFile.c @@ -0,0 +1,380 @@ +/* + * 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 "../parc_RandomAccessFile.c" + +#include <sys/param.h> + +#include <fcntl.h> + +#include <LongBow/testing.h> +#include <LongBow/debugging.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_SafeMemory.h> +#include <parc/algol/parc_DisplayIndented.h> + +#include <parc/testing/parc_MemoryTesting.h> +#include <parc/testing/parc_ObjectTesting.h> + +LONGBOW_TEST_RUNNER(parcRandomAccessFile_RandomAccessFile) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(CreateAcquireRelease); + LONGBOW_RUN_TEST_FIXTURE(Object); + LONGBOW_RUN_TEST_FIXTURE(Specialization); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parcRandomAccessFile_RandomAccessFile) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parcRandomAccessFile_RandomAccessFile) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(CreateAcquireRelease) +{ + LONGBOW_RUN_TEST_CASE(CreateAcquireRelease, CreateRelease); +} + +LONGBOW_TEST_FIXTURE_SETUP(CreateAcquireRelease) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(CreateAcquireRelease) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(CreateAcquireRelease, CreateRelease) +{ + char dirname[] = "/tmp/RandomAccessFile_XXXXXX"; + char filename[MAXPATHLEN]; + + char *temporaryDirectory = mkdtemp(dirname); + assertNotNull(temporaryDirectory, "tmp_dirname should not be null"); + sprintf(filename, "%s/tmpfile", temporaryDirectory); + + PARCFile *file = parcFile_Create(filename); + PARCRandomAccessFile *instance = parcRandomAccessFile_Open(file); + assertNotNull(instance, "Expected non-null result from parcRandomAccessFile_Open();"); + + parcObjectTesting_AssertAcquireReleaseContract(parcRandomAccessFile_Acquire, instance); + + parcRandomAccessFile_Release(&instance); + assertNull(instance, "Expected null result from parcRandomAccessFile_Release();"); + + parcFile_Release(&file); +} + +LONGBOW_TEST_FIXTURE(Object) +{ + LONGBOW_RUN_TEST_CASE(Object, parcRandomAccessFile_Display); + // XXX: Disable this test until fixed + //LONGBOW_RUN_TEST_CASE(Object, parcRandomAccessFile_Equals); + LONGBOW_RUN_TEST_CASE(Object, parcRandomAccessFile_IsValid); + LONGBOW_RUN_TEST_CASE(Object, parcRandomAccessFile_ToJSON); + LONGBOW_RUN_TEST_CASE(Object, parcRandomAccessFile_ToString); +} + +LONGBOW_TEST_FIXTURE_SETUP(Object) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Object) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s mismanaged memory.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Object, parcRandomAccessFile_Display) +{ + char dirname[] = "/tmp/RandomAccessFile_XXXXXX"; + char filename[MAXPATHLEN]; + + char *temporaryDirectory = mkdtemp(dirname); + assertNotNull(temporaryDirectory, "tmp_dirname should not be null"); + sprintf(filename, "%s/tmpfile", temporaryDirectory); + + PARCFile *file = parcFile_Create(filename); + PARCRandomAccessFile *instance = parcRandomAccessFile_Open(file); + parcFile_Release(&file); + + parcRandomAccessFile_Display(instance, 0); + parcRandomAccessFile_Release(&instance); +} + +LONGBOW_TEST_CASE(Object, parcRandomAccessFile_Equals) +{ + char dirname[] = "/tmp/RandomAccessFile_XXXXXX"; + char filename[MAXPATHLEN]; + + char *temporaryDirectory = mkdtemp(dirname); + assertNotNull(temporaryDirectory, "tmp_dirname should not be null"); + + sprintf(filename, "%s/tmpfileX", temporaryDirectory); + PARCFile *fileX = parcFile_Create(filename); + + sprintf(filename, "%s/tmpfileY", temporaryDirectory); + PARCFile *fileY = parcFile_Create(filename); + + sprintf(filename, "%s/tmpfileZ", temporaryDirectory); + PARCFile *fileZ = parcFile_Create(filename); + + PARCRandomAccessFile *x = parcRandomAccessFile_Open(fileX); + PARCRandomAccessFile *y = parcRandomAccessFile_Open(fileY); + PARCRandomAccessFile *z = parcRandomAccessFile_Open(fileZ); + parcFile_Release(&fileX); + parcFile_Release(&fileY); + parcFile_Release(&fileZ); + + parcObjectTesting_AssertEquals(x, y, z, NULL); + + parcRandomAccessFile_Close(x); + parcRandomAccessFile_Close(y); + parcRandomAccessFile_Close(z); + + parcRandomAccessFile_Release(&x); + parcRandomAccessFile_Release(&y); + parcRandomAccessFile_Release(&z); +} + +LONGBOW_TEST_CASE(Object, parcRandomAccessFile_IsValid) +{ + char dirname[] = "/tmp/RandomAccessFile_XXXXXX"; + char filename[MAXPATHLEN]; + + char *temporaryDirectory = mkdtemp(dirname); + assertNotNull(temporaryDirectory, "tmp_dirname should not be null"); + sprintf(filename, "%s/tmpfile", temporaryDirectory); + + PARCFile *file = parcFile_Create(filename); + parcFile_CreateNewFile(file); + + PARCRandomAccessFile *instance = parcRandomAccessFile_Open(file); + parcFile_Release(&file); + assertTrue(parcRandomAccessFile_IsValid(instance), "Expected parcRandomAccessFile_Create to result in a valid instance."); + + parcRandomAccessFile_Release(&instance); + assertFalse(parcRandomAccessFile_IsValid(instance), "Expected parcRandomAccessFile_Release to result in an invalid instance."); +} + +LONGBOW_TEST_CASE(Object, parcRandomAccessFile_ToJSON) +{ + char dirname[] = "/tmp/RandomAccessFile_XXXXXX"; + char filename[MAXPATHLEN]; + + char *temporaryDirectory = mkdtemp(dirname); + assertNotNull(temporaryDirectory, "tmp_dirname should not be null"); + sprintf(filename, "%s/tmpfile", temporaryDirectory); + + PARCFile *file = parcFile_Create(filename); + PARCRandomAccessFile *instance = parcRandomAccessFile_Open(file); + parcFile_Release(&file); + + PARCJSON *json = parcRandomAccessFile_ToJSON(instance); + + const PARCJSONPair *pair = parcJSON_GetPairByName(json, "fname"); + PARCJSONValue *value = parcJSONPair_GetValue(pair); + PARCBuffer *buffer = parcJSONValue_GetString(value); + + char *string = parcBuffer_ToString(buffer); + assertTrue(strcmp(filename, string) == 0, "The file was stored correctly"); + + parcMemory_Deallocate(&string); + + parcJSON_Release(&json); + + parcRandomAccessFile_Release(&instance); +} + +LONGBOW_TEST_CASE(Object, parcRandomAccessFile_ToString) +{ + char dirname[] = "/tmp/RandomAccessFile_XXXXXX"; + char filename[MAXPATHLEN]; + + char *temporaryDirectory = mkdtemp(dirname); + assertNotNull(temporaryDirectory, "tmp_dirname should not be null"); + sprintf(filename, "%s/tmpfile", temporaryDirectory); + + PARCFile *file = parcFile_Create(filename); + PARCRandomAccessFile *instance = parcRandomAccessFile_Open(file); + parcFile_Release(&file); + + char *string = parcRandomAccessFile_ToString(instance); + + assertNotNull(string, "Expected non-NULL result from parcRandomAccessFile_ToString"); + + parcMemory_Deallocate((void **) &string); + parcRandomAccessFile_Release(&instance); +} + +LONGBOW_TEST_FIXTURE(Specialization) +{ + LONGBOW_RUN_TEST_CASE(Object, parcRandomAccessFile_Read); + LONGBOW_RUN_TEST_CASE(Object, parcRandomAccessFile_Write); + LONGBOW_RUN_TEST_CASE(Object, parcRandomAccessFile_Seek); +} + +LONGBOW_TEST_FIXTURE_SETUP(Specialization) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Specialization) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s mismanaged memory.", longBowTestCase_GetFullName(testCase))) { + parcSafeMemory_ReportAllocation(STDOUT_FILENO); + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Object, parcRandomAccessFile_Read) +{ + char *fname = "tmpfile"; + + PARCFile *file = parcFile_Create(fname); + + parcFile_CreateNewFile(file); + FILE *fp = fopen(fname, "w"); + fseek(fp, 0, SEEK_SET); + + uint8_t data[128]; + for (int i = 0; i < 128; i++) { + data[i] = i; + } + fwrite(data, 1, 128, fp); + fclose(fp); + + PARCRandomAccessFile *instance = parcRandomAccessFile_Open(file); + parcFile_Release(&file); + + PARCBuffer *buffer = parcBuffer_Allocate(128); + size_t numBytes = parcRandomAccessFile_Read(instance, buffer); + assertTrue(numBytes == 128, "Expected 128 bytes to be read, but got %zu", numBytes); + + parcBuffer_Flip(buffer); + uint8_t *bytes = parcBuffer_Overlay(buffer, parcBuffer_Remaining(buffer)); + assertTrue(memcmp(data, bytes, 128) == 0, "Expected buffers to be equal"); + + parcBuffer_Release(&buffer); + parcRandomAccessFile_Close(instance); + parcRandomAccessFile_Release(&instance); +} + +LONGBOW_TEST_CASE(Object, parcRandomAccessFile_Write) +{ + char *fname = "tmpfile"; + + PARCFile *file = parcFile_Create(fname); + + parcFile_CreateNewFile(file); + + uint8_t data[128]; + for (int i = 0; i < 128; i++) { + data[i] = i; + } + + PARCRandomAccessFile *instance = parcRandomAccessFile_Open(file); + PARCBuffer *buffer = parcBuffer_Allocate(128); + parcBuffer_PutArray(buffer, 128, data); + parcBuffer_Flip(buffer); + size_t numBytes = parcRandomAccessFile_Write(instance, buffer); + assertTrue(numBytes == 128, "Expected 128 bytes to be read, but got %zu", numBytes); + parcBuffer_Release(&buffer); + + parcRandomAccessFile_Close(instance); + parcRandomAccessFile_Release(&instance); + + uint8_t bytes[128]; + FILE *fp = fopen(fname, "r"); + numBytes = fread(bytes, 1, 128, fp); + assertTrue(numBytes == 128, "Expected 128 bytes to be read, but got %zu", numBytes); + + fclose(fp); + + assertTrue(memcmp(data, bytes, 128) == 0, "Expected buffers to be equal"); + + parcFile_Release(&file); +} + +LONGBOW_TEST_CASE(Object, parcRandomAccessFile_Seek) +{ + char *fname = "tmpfile"; + + PARCFile *file = parcFile_Create(fname); + + parcFile_CreateNewFile(file); + FILE *fp = fopen(fname, "w"); + fseek(fp, 0, SEEK_SET); + + uint8_t data[128]; + for (int i = 0; i < 128; i++) { + data[i] = i; + } + fwrite(data, 1, 128, fp); + fclose(fp); + + PARCRandomAccessFile *instance = parcRandomAccessFile_Open(file); + PARCBuffer *buffer = parcBuffer_Allocate(128); + parcRandomAccessFile_Seek(instance, 64, PARCRandomAccessFilePosition_Start); + size_t numBytes = parcRandomAccessFile_Read(instance, buffer); + assertTrue(numBytes == 64, "Expected 64 bytes to be read, but got %zu", numBytes); + + parcRandomAccessFile_Seek(instance, 0, PARCRandomAccessFilePosition_End); + parcBuffer_Flip(buffer); + numBytes = parcRandomAccessFile_Read(instance, buffer); + assertTrue(numBytes == 0, "Expected 0 bytes to be read, but got %zu", numBytes); + + parcRandomAccessFile_Seek(instance, 0, PARCRandomAccessFilePosition_Start); + parcBuffer_Flip(buffer); + numBytes = parcRandomAccessFile_Read(instance, buffer); + assertTrue(numBytes == 128, "Expected 128 bytes to be read, but got %zu", numBytes); + + parcBuffer_Release(&buffer); + parcRandomAccessFile_Close(instance); + parcRandomAccessFile_Release(&instance); + + parcFile_Release(&file); +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parcRandomAccessFile_RandomAccessFile); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_ReadOnlyBuffer.c b/libparc/parc/algol/test/test_parc_ReadOnlyBuffer.c new file mode 100644 index 00000000..19e250f8 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_ReadOnlyBuffer.c @@ -0,0 +1,689 @@ +/* + * 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 the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../parc_ReadOnlyBuffer.c" + +#include <stdio.h> +#include <inttypes.h> + +#include <LongBow/unit-test.h> +#include <LongBow/debugging.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_SafeMemory.h> +#include <parc/testing/parc_ObjectTesting.h> + +LONGBOW_TEST_RUNNER(parc_ReadableBuffer) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Getters); + LONGBOW_RUN_TEST_FIXTURE(CreateDestroy); + LONGBOW_RUN_TEST_FIXTURE(Errors); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_ReadableBuffer) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_ReadableBuffer) +{ + return LONGBOW_STATUS_SUCCEEDED; +} +LONGBOW_TEST_FIXTURE(CreateDestroy) +{ + LONGBOW_RUN_TEST_CASE(CreateDestroy, parcReadOnlyBuffer_Create); + LONGBOW_RUN_TEST_CASE(CreateDestroy, parcReadOnlyBuffer_Allocate_AcquireRelease); +// LONGBOW_RUN_TEST_CASE(CreateDestroy, parcReadOnlyBuffer_Allocate_AcquireRelease_TooMany); + LONGBOW_RUN_TEST_CASE(CreateDestroy, parcReadOnlyBuffer_Wrap); + LONGBOW_RUN_TEST_CASE(CreateDestroy, parcReadOnlyBuffer_Wrap_NULL); + LONGBOW_RUN_TEST_CASE(CreateDestroy, parcReadOnlyBuffer_Wrap_WithOffset); +} + +LONGBOW_TEST_FIXTURE_SETUP(CreateDestroy) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(CreateDestroy) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(CreateDestroy, parcReadOnlyBuffer_Create) +{ + PARCBuffer *buffer = parcBuffer_Allocate(10); + PARCReadOnlyBuffer *actual = parcReadOnlyBuffer_Create(buffer); + + assertTrue(parcReadOnlyBuffer_Position(actual) == 0, "Expected initial position to be 0."); + assertTrue(parcReadOnlyBuffer_Limit(actual) == 10, "Expected initial limit to be 10."); + + parcReadOnlyBuffer_Release(&actual); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(CreateDestroy, parcReadOnlyBuffer_Wrap_NULL) +{ + PARCReadOnlyBuffer *actual = parcReadOnlyBuffer_Create(parcBuffer_Wrap(NULL, 10, 0, 10)); + assertNull(actual, "Expected parcReadOnlyBuffer_Wrap to return NULL"); +} + +LONGBOW_TEST_CASE(CreateDestroy, parcReadOnlyBuffer_Wrap) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + PARCBuffer *buffer = parcBuffer_Wrap(array, 10, 0, 10); + + PARCReadOnlyBuffer *actual = parcReadOnlyBuffer_Create(buffer); + assertTrue(parcReadOnlyBuffer_Position(actual) == 0, "Expected initial position to be 0."); + assertTrue(parcReadOnlyBuffer_Limit(actual) == sizeof(array) / sizeof(array[0]), "Expected initial limit to be 10."); + + parcReadOnlyBuffer_Release(&actual); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(CreateDestroy, parcReadOnlyBuffer_Wrap_WithOffset) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCBuffer *buffer = parcBuffer_Wrap(array, 10, 3, 10); + + PARCReadOnlyBuffer *actual = parcReadOnlyBuffer_Create(buffer); + parcBuffer_Release(&buffer); + assertTrue(parcReadOnlyBuffer_Capacity(actual) == 10, "Expected initial capacity to be 3."); + assertTrue(parcReadOnlyBuffer_Limit(actual) == 10, "Expected initial limit to be 3."); + assertTrue(parcReadOnlyBuffer_Position(actual) == 3, "Expected initial position to be 0."); + + parcReadOnlyBuffer_Release(&actual); +} + +LONGBOW_TEST_CASE(CreateDestroy, parcReadOnlyBuffer_Allocate_AcquireRelease) +{ + PARCBuffer *buffer = parcBuffer_Allocate(10); + + PARCReadOnlyBuffer *expected = parcReadOnlyBuffer_Create(buffer); + PARCReadOnlyBuffer *actual = parcReadOnlyBuffer_Acquire(expected); + + assertTrue(expected == actual, "Expected %p, actual %p", (void *) expected, (void *) actual); + + parcReadOnlyBuffer_Release(&expected); + assertTrue(expected == NULL, "Expected parcReadOnlyBuffer_Release to NULL the pointer."); + parcReadOnlyBuffer_Release(&actual); + assertTrue(actual == NULL, "Expected parcReadOnlyBuffer_Release to NULL the pointer."); + parcBuffer_Release(&buffer); +} + +//LONGBOW_TEST_CASE_EXPECTS(CreateDestroy, parcReadOnlyBuffer_Allocate_AcquireRelease_TooMany, .event = &LongBowTrapIllegalValue) +//{ +// PARCBuffer *buffer = parcBuffer_Allocate(10); +// PARCReadOnlyBuffer *expected = parcReadOnlyBuffer_Create(buffer); +// PARCReadOnlyBuffer *actual = parcReadOnlyBuffer_Acquire(expected); +// PARCReadOnlyBuffer *alias = actual; +// +// parcBuffer_Release(&buffer); +// parcReadOnlyBuffer_Release(&expected); +// parcReadOnlyBuffer_Release(&actual); +// parcReadOnlyBuffer_Release(&alias); // this must fail. +//} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcReadOnlyBuffer_Array); + LONGBOW_RUN_TEST_CASE(Global, parcReadOnlyBuffer_ArrayOffset); + LONGBOW_RUN_TEST_CASE(Global, parcReadOnlyBuffer_Clear); + LONGBOW_RUN_TEST_CASE(Global, parcReadOnlyBuffer_Copy); + LONGBOW_RUN_TEST_CASE(Global, parcReadOnlyBuffer_Equals); + LONGBOW_RUN_TEST_CASE(Global, parcReadOnlyBuffer_Flip); + LONGBOW_RUN_TEST_CASE(Global, parcReadOnlyBuffer_GetByte); + LONGBOW_RUN_TEST_CASE(Global, parcReadOnlyBuffer_GetArray); + LONGBOW_RUN_TEST_CASE(Global, parcReadOnlyBuffer_HasRemaining); + LONGBOW_RUN_TEST_CASE(Global, parcReadOnlyBuffer_HashCode); + LONGBOW_RUN_TEST_CASE(Global, parcReadOnlyBuffer_Mark); + LONGBOW_RUN_TEST_CASE(Global, parcReadOnlyBuffer_Overlay); + LONGBOW_RUN_TEST_CASE(Global, parcReadOnlyBuffer_Position); + LONGBOW_RUN_TEST_CASE(Global, parcReadOnlyBuffer_Remaining); + LONGBOW_RUN_TEST_CASE(Global, parcReadOnlyBuffer_Rewind); + LONGBOW_RUN_TEST_CASE(Global, parcReadOnlyBuffer_SetLimit); + LONGBOW_RUN_TEST_CASE(Global, parcReadOnlyBuffer_SetLimit_TruncatePosition); + LONGBOW_RUN_TEST_CASE(Global, parcReadOnlyBuffer_SetPosition); + LONGBOW_RUN_TEST_CASE(Global, parcReadOnlyBuffer_ToString); + LONGBOW_RUN_TEST_CASE(Global, parcReadOnlyBuffer_Display); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcReadOnlyBuffer_Equals) +{ + PARCReadOnlyBuffer *x = parcReadOnlyBuffer_Wrap((uint8_t [10]) { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 10, 0, 10); + PARCReadOnlyBuffer *y = parcReadOnlyBuffer_Wrap((uint8_t [10]) { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 10, 0, 10); + PARCReadOnlyBuffer *z = parcReadOnlyBuffer_Wrap((uint8_t [10]) { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 10, 0, 10); + PARCReadOnlyBuffer *u1 = parcReadOnlyBuffer_Wrap((uint8_t [10]) { 0, 1, 2, 3, 4, 5, 6, 7, 8, 10 }, 10, 0, 10); + PARCReadOnlyBuffer *u2 = parcReadOnlyBuffer_Wrap((uint8_t [10]) { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 9, 0, 9); + PARCReadOnlyBuffer *u3 = parcReadOnlyBuffer_Wrap((uint8_t [9]) { 0, 1, 2, 3, 4, 5, 6, 7, 8 }, 9, 0, 9); + PARCReadOnlyBuffer *u4 = parcReadOnlyBuffer_SetPosition(parcReadOnlyBuffer_Wrap((uint8_t [9]) { 0, 1, 2, 3, 4, 5, 6, 7, 8 }, 9, 0, 9), 2); + PARCReadOnlyBuffer *u5 = parcReadOnlyBuffer_SetPosition(parcReadOnlyBuffer_Wrap((uint8_t [9]) { 0, 1, 2, 3, 4, 5, 6, 7, 8 }, 9, 0, 9), 9); + PARCReadOnlyBuffer *u6 = parcReadOnlyBuffer_SetPosition(parcReadOnlyBuffer_Wrap((uint8_t [9]) { 0, 1, 2, 3, 4, 5, 6, 7, 8 }, 9, 0, 9), 9); + + parcObjectTesting_AssertEqualsFunction(parcReadOnlyBuffer_Equals, x, y, z, u1, u2, u3, u4, u5, u6, NULL); + + parcReadOnlyBuffer_Release(&x); + parcReadOnlyBuffer_Release(&y); + parcReadOnlyBuffer_Release(&z); + parcReadOnlyBuffer_Release(&u1); + parcReadOnlyBuffer_Release(&u2); + parcReadOnlyBuffer_Release(&u3); + parcReadOnlyBuffer_Release(&u4); + parcReadOnlyBuffer_Release(&u5); + parcReadOnlyBuffer_Release(&u6); +} + +LONGBOW_TEST_CASE(Global, parcReadOnlyBuffer_Array) +{ + uint8_t expected[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCBuffer *readWriteBuffer = parcBuffer_Wrap(expected, 10, 0, 10); + PARCReadOnlyBuffer *buffer = parcReadOnlyBuffer_Create(readWriteBuffer); + parcBuffer_Release(&readWriteBuffer); + + PARCByteArray *array = parcReadOnlyBuffer_Array(buffer); + uint8_t *actual = parcByteArray_Array(array); + + parcReadOnlyBuffer_Release(&buffer); + + assertTrue(expected == actual, + "Expected %p, actual %p", + (void *) expected, (void *) actual); +} + +LONGBOW_TEST_CASE(Global, parcReadOnlyBuffer_Flip) +{ + uint8_t expected[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCBuffer *buffer = parcBuffer_PutArray(parcBuffer_Allocate(10), 10, expected); + + PARCReadOnlyBuffer *actual = parcReadOnlyBuffer_Create(buffer); + + parcReadOnlyBuffer_Flip(actual); + assertTrue(parcReadOnlyBuffer_Position(actual) == 0, + "Expected position to be 0."); + assertTrue(parcReadOnlyBuffer_Limit(actual) == 10, + "Expected limit to be 10."); + + parcReadOnlyBuffer_Release(&actual); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcReadOnlyBuffer_Copy) +{ + uint8_t expected[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCBuffer *buffer = parcBuffer_PutArray(parcBuffer_Allocate(10), 10, expected); + + PARCReadOnlyBuffer *original = parcReadOnlyBuffer_Create(buffer); + + PARCReadOnlyBuffer *copy = parcReadOnlyBuffer_Copy(original); + + assertTrue(parcReadOnlyBuffer_Equals(original, copy), "Expected the copy to be equal to the original."); + + parcReadOnlyBuffer_Release(©); + parcReadOnlyBuffer_Release(&original); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcReadOnlyBuffer_Clear) +{ + uint8_t expected[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCBuffer *buffer = parcBuffer_PutArray(parcBuffer_Allocate(10), 10, expected); + + PARCReadOnlyBuffer *actual = parcReadOnlyBuffer_Create(buffer); + assertTrue(parcReadOnlyBuffer_Position(actual) == 10, "Expected position to be 10."); + assertTrue(parcReadOnlyBuffer_Limit(actual) == 10, "Expected limit to be 10."); + + parcReadOnlyBuffer_Clear(actual); + assertTrue(parcReadOnlyBuffer_Position(actual) == 0, "Expected position to be 0."); + assertTrue(parcReadOnlyBuffer_Limit(actual) == 10, "Expected limit to be 10."); + + parcBuffer_Release(&buffer); + parcReadOnlyBuffer_Release(&actual); +} + +LONGBOW_TEST_CASE(Global, parcReadOnlyBuffer_ArrayOffset) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + size_t expected = 5; + PARCReadOnlyBuffer *buffer = parcReadOnlyBuffer_Wrap(array, 10, expected, 10); + + size_t actual = parcReadOnlyBuffer_ArrayOffset(buffer); + parcReadOnlyBuffer_Release(&buffer); + + assertTrue(0 == actual, + "Expected offset to be 0, actual %zu", actual); +} + +LONGBOW_TEST_CASE(Global, parcReadOnlyBuffer_Position) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCReadOnlyBuffer *buffer = parcReadOnlyBuffer_Wrap(array, 10, 0, 10); + + size_t expected = 5; + parcReadOnlyBuffer_SetPosition(buffer, expected); + + size_t actual = parcReadOnlyBuffer_Position(buffer); + + assertTrue(expected == actual, + "Expected position to be 0, actual %zu", actual); + parcReadOnlyBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcReadOnlyBuffer_Overlay) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + uint8_t expected[5] = { 5, 6, 7, 8, 9 }; + + PARCReadOnlyBuffer *buffer = parcReadOnlyBuffer_Wrap(array, 10, 0, 10); + + size_t position = 5; + parcReadOnlyBuffer_SetPosition(buffer, position); + uint8_t *actual = parcReadOnlyBuffer_Overlay(buffer, sizeof(array) - position); + + assertTrue(memcmp(expected, actual, sizeof(expected)) == 0, + "Array contents should not be different."); + parcReadOnlyBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcReadOnlyBuffer_SetPosition) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCReadOnlyBuffer *buffer = parcReadOnlyBuffer_Wrap(array, 10, 0, 10); + + size_t expected = 2; + parcReadOnlyBuffer_SetPosition(buffer, expected); + size_t actual = parcReadOnlyBuffer_Position(buffer); + + assertTrue(expected == actual, "Expected %zd, actual %zd", expected, actual); + + parcReadOnlyBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcReadOnlyBuffer_SetLimit) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCReadOnlyBuffer *buffer = parcReadOnlyBuffer_Wrap(array, 10, 0, 10); + + size_t expected = 2; + parcReadOnlyBuffer_SetLimit(buffer, expected); + size_t actual = parcReadOnlyBuffer_Limit(buffer); + + assertTrue(expected == actual, "Expected %zd, actual %zd", expected, actual); + + parcReadOnlyBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcReadOnlyBuffer_SetLimit_TruncatePosition) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCReadOnlyBuffer *buffer = parcReadOnlyBuffer_Wrap(array, 10, 0, 10); + + parcReadOnlyBuffer_SetPosition(buffer, 5); + parcReadOnlyBuffer_Mark(buffer); + + size_t expected = 2; + parcReadOnlyBuffer_SetLimit(buffer, expected); + size_t actual = parcReadOnlyBuffer_Limit(buffer); + + assertTrue(expected == actual, "Expected %zd, actual %zd", expected, actual); + parcReadOnlyBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcReadOnlyBuffer_Remaining) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCReadOnlyBuffer *buffer = parcReadOnlyBuffer_Wrap(array, 10, 0, 10); + + size_t expected = 10; + size_t actual = parcReadOnlyBuffer_Remaining(buffer); + + assertTrue(expected == actual, "Expected %zd, actual %zd", expected, actual); + parcReadOnlyBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcReadOnlyBuffer_HasRemaining) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCReadOnlyBuffer *buffer = parcReadOnlyBuffer_Wrap(array, 10, 0, 10); + bool actual = parcReadOnlyBuffer_HasRemaining(buffer); + + assertTrue(actual, "Expected true"); + parcReadOnlyBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcReadOnlyBuffer_Rewind) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + PARCReadOnlyBuffer *buffer = parcReadOnlyBuffer_Wrap(array, 10, 0, 10); + parcReadOnlyBuffer_SetPosition(buffer, 4); + size_t actual = parcReadOnlyBuffer_Position(buffer); + assertTrue(actual == 4, "Expected position to be at 4."); + + parcReadOnlyBuffer_Rewind(buffer); + + actual = parcReadOnlyBuffer_Position(buffer); + assertTrue(actual == 0, "Expected position to be at 0."); + parcReadOnlyBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcReadOnlyBuffer_Mark) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + PARCReadOnlyBuffer *buffer = parcReadOnlyBuffer_Wrap(array, 10, 0, 10); + + size_t expected = 2; + parcReadOnlyBuffer_SetPosition(buffer, expected); + parcReadOnlyBuffer_Mark(buffer); + parcReadOnlyBuffer_SetPosition(buffer, 4); + parcReadOnlyBuffer_Reset(buffer); + size_t actual = parcReadOnlyBuffer_Position(buffer); + + assertTrue(expected == actual, "Expected %zd, actual %zd", expected, actual); + parcReadOnlyBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcReadOnlyBuffer_GetByte) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + PARCReadOnlyBuffer *buffer = parcReadOnlyBuffer_Wrap(array, 10, 0, 10); + + uint8_t actual = parcReadOnlyBuffer_GetUint8(buffer); + + assertTrue(array[0] == actual, + "Expected %d, actual %d", array[0], actual); + + parcReadOnlyBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcReadOnlyBuffer_GetArray) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + PARCReadOnlyBuffer *buffer = parcReadOnlyBuffer_Wrap(array, 10, 0, 10); + + uint8_t actual[10]; + + parcReadOnlyBuffer_GetArray(buffer, actual, sizeof(actual)); + + assertTrue(memcmp(array, actual, sizeof(actual)) == 0, + "Expected arrays to be equal."); + + parcReadOnlyBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcReadOnlyBuffer_HashCode) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + PARCBuffer *referenceBuffer = parcBuffer_Wrap(array, 10, 0, 10); + + PARCReadOnlyBuffer *buffer1 = parcReadOnlyBuffer_Wrap(array, 10, 0, 10); + + PARCReadOnlyBuffer *buffer2 = parcReadOnlyBuffer_Wrap(array, 10, 0, 10); + + uint32_t hashX = parcReadOnlyBuffer_HashCode(buffer1); + uint32_t hashY = parcReadOnlyBuffer_HashCode(buffer2); + uint32_t referenceHash = parcBuffer_HashCode(referenceBuffer); + + assertTrue(hashX == hashY, "Expected %u, actual %u", hashX, hashY); + assertTrue(hashX == referenceHash, "Expected %u, actual %u", hashX, hashY); + + parcReadOnlyBuffer_Release(&buffer2); + parcReadOnlyBuffer_Release(&buffer1); + parcBuffer_Release(&referenceBuffer); +} + +LONGBOW_TEST_CASE(Global, parcReadOnlyBuffer_ToString) +{ + uint8_t array[] = { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', 'x' }; + + PARCReadOnlyBuffer *buffer = parcReadOnlyBuffer_Wrap(array, sizeof(array), 0, sizeof(array)); + + char *actual = parcReadOnlyBuffer_ToString(buffer); + + assertTrue(strcmp("hello worldx", actual) == 0, "Expected 'hello world', actual %s", actual); + + parcMemory_Deallocate((void **) &actual); + + parcReadOnlyBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, parcReadOnlyBuffer_Display) +{ + uint8_t array[] = { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', 'x' }; + + PARCReadOnlyBuffer *buffer = parcReadOnlyBuffer_Wrap(array, sizeof(array), 0, sizeof(array)); + + parcReadOnlyBuffer_Display(buffer, 0); + + parcReadOnlyBuffer_Release(&buffer); +} + +LONGBOW_TEST_FIXTURE(Getters) +{ + LONGBOW_RUN_TEST_CASE(Getters, parcReadOnlyBuffer_GetUint8); + LONGBOW_RUN_TEST_CASE(Getters, parcReadOnlyBuffer_GetUint16); + LONGBOW_RUN_TEST_CASE(Getters, parcReadOnlyBuffer_GetUint32); + LONGBOW_RUN_TEST_CASE(Getters, parcReadOnlyBuffer_GetUint64); + LONGBOW_RUN_TEST_CASE(Getters, parcReadOnlyBuffer_GetAtIndex); +} + +LONGBOW_TEST_FIXTURE_SETUP(Getters) +{ + PARCBuffer *buffer = parcBuffer_Allocate(100); + + longBowTestCase_SetClipBoardData(testCase, buffer); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Getters) +{ + PARCBuffer *buffer = longBowTestCase_GetClipBoardData(testCase); + parcBuffer_Release(&buffer); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Getters, parcReadOnlyBuffer_GetAtIndex) +{ + PARCBuffer *buffer = parcBuffer_Allocate(sizeof(uint8_t)); + uint8_t expected = 0x12; + + parcBuffer_PutUint8(buffer, expected); + parcBuffer_Flip(buffer); + + PARCReadOnlyBuffer *readOnly = parcReadOnlyBuffer_Create(buffer); + uint8_t actual = parcReadOnlyBuffer_GetAtIndex(readOnly, 0); + + parcReadOnlyBuffer_Release(&readOnly); + parcBuffer_Release(&buffer); + assertTrue(expected == actual, "Expected %d, actual %d", expected, actual); +} + +LONGBOW_TEST_CASE(Getters, parcReadOnlyBuffer_GetUint8) +{ + PARCBuffer *buffer = parcBuffer_Allocate(sizeof(uint8_t)); + uint8_t expected = 0x12; + + parcBuffer_PutUint8(buffer, expected); + parcBuffer_Flip(buffer); + + PARCReadOnlyBuffer *readOnly = parcReadOnlyBuffer_Create(buffer); + uint8_t actual = parcReadOnlyBuffer_GetUint8(readOnly); + + parcReadOnlyBuffer_Release(&readOnly); + parcBuffer_Release(&buffer); + assertTrue(expected == actual, "Expected %d, actual %d", expected, actual); +} + +LONGBOW_TEST_CASE(Getters, parcReadOnlyBuffer_GetUint16) +{ + PARCBuffer *buffer = parcBuffer_Allocate(sizeof(uint16_t)); + uint16_t expected = 0x1234; + + parcBuffer_PutUint16(buffer, expected); + parcBuffer_Flip(buffer); + + PARCReadOnlyBuffer *readOnly = parcReadOnlyBuffer_Create(buffer); + uint16_t actual = parcReadOnlyBuffer_GetUint16(readOnly); + + parcReadOnlyBuffer_Release(&readOnly); + parcBuffer_Release(&buffer); + assertTrue(expected == actual, "Expected %d, actual %d", expected, actual); +} + +LONGBOW_TEST_CASE(Getters, parcReadOnlyBuffer_GetUint32) +{ + PARCBuffer *buffer = parcBuffer_Allocate(sizeof(uint32_t)); + uint32_t expected = 0x12345678; + + parcBuffer_PutUint32(buffer, expected); + parcBuffer_Flip(buffer); + + PARCReadOnlyBuffer *readOnly = parcReadOnlyBuffer_Create(buffer); + uint32_t actual = parcReadOnlyBuffer_GetUint32(readOnly); + + parcReadOnlyBuffer_Release(&readOnly); + parcBuffer_Release(&buffer); + assertTrue(expected == actual, "Expected %d, actual %d", expected, actual); +} + +LONGBOW_TEST_CASE(Getters, parcReadOnlyBuffer_GetUint64) +{ + PARCBuffer *buffer = parcBuffer_Allocate(sizeof(uint64_t)); + uint64_t expected = 0x1234567812345678; + + parcBuffer_PutUint64(buffer, expected); + parcBuffer_Flip(buffer); + + PARCReadOnlyBuffer *readOnly = parcReadOnlyBuffer_Create(buffer); + uint64_t actual = parcReadOnlyBuffer_GetUint64(readOnly); + + parcReadOnlyBuffer_Release(&readOnly); + parcBuffer_Release(&buffer); + assertTrue(expected == actual, "Expected %" PRIu64 ", actual %" PRIu64 "", expected, actual); +} + +LONGBOW_TEST_FIXTURE(Errors) +{ + LONGBOW_RUN_TEST_CASE(Errors, parcReadOnlyBuffer_GetByte_Underflow); + LONGBOW_RUN_TEST_CASE(Errors, parcReadOnlyBuffer_Mark_mark_exceeds_position); +} + +typedef struct parc_buffer_longbow_clipboard { + PARCReadOnlyBuffer *buffer; +} parcReadOnlyBuffer_LongBowClipBoard; + +LONGBOW_TEST_FIXTURE_SETUP(Errors) +{ + uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + parcReadOnlyBuffer_LongBowClipBoard *testData = calloc(1, sizeof(parcReadOnlyBuffer_LongBowClipBoard)); + testData->buffer = parcReadOnlyBuffer_Wrap(array, sizeof(array), 0, sizeof(array)); + + longBowTestCase_SetClipBoardData(testCase, testData); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Errors) +{ + parcReadOnlyBuffer_LongBowClipBoard *testData = longBowTestCase_GetClipBoardData(testCase); + parcReadOnlyBuffer_Release(&testData->buffer); + free(testData); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE_EXPECTS(Errors, parcReadOnlyBuffer_GetByte_Underflow, .event = &LongBowTrapOutOfBounds) +{ + parcReadOnlyBuffer_LongBowClipBoard *testData = longBowTestCase_GetClipBoardData(testCase); + PARCReadOnlyBuffer *buffer = testData->buffer; + + parcReadOnlyBuffer_SetPosition(buffer, 10); + parcReadOnlyBuffer_GetUint8(buffer); // this will fail. +} + +LONGBOW_TEST_CASE_EXPECTS(Errors, parcReadOnlyBuffer_Mark_mark_exceeds_position, .event = &LongBowAssertEvent) +{ + parcReadOnlyBuffer_LongBowClipBoard *testData = longBowTestCase_GetClipBoardData(testCase); + PARCReadOnlyBuffer *buffer = testData->buffer; + + size_t expected = 2; + parcReadOnlyBuffer_SetPosition(buffer, expected); + parcReadOnlyBuffer_Mark(buffer); + parcReadOnlyBuffer_SetPosition(buffer, 0); + parcReadOnlyBuffer_Reset(buffer); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_ReadableBuffer); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_SafeMemory.c b/libparc/parc/algol/test/test_parc_SafeMemory.c new file mode 100644 index 00000000..53c0b3c8 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_SafeMemory.c @@ -0,0 +1,813 @@ +/* + * 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 "../parc_SafeMemory.c" + +#include <LongBow/unit-test.h> + +#include <fcntl.h> + +LONGBOW_TEST_RUNNER(safetyMemory) +{ + LONGBOW_RUN_TEST_FIXTURE(Static); + LONGBOW_RUN_TEST_FIXTURE(ReportAllocation); + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Errors); + + LONGBOW_RUN_TEST_FIXTURE(Performance); +} + +LONGBOW_TEST_RUNNER_SETUP(safetyMemory) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_RUNNER_TEARDOWN(safetyMemory) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Static) +{ + LONGBOW_RUN_TEST_CASE(Static, PARCSafeMemory_Report); + LONGBOW_RUN_TEST_CASE(Static, _parcSafeMemory_StateToString); + LONGBOW_RUN_TEST_CASE(Static, _parcSafeMemory_GetPrefixState_OK); + LONGBOW_RUN_TEST_CASE(Static, _parcSafeMemory_GetPrefixState_ALREADYFREE); + LONGBOW_RUN_TEST_CASE(Static, _parcSafeMemory_GetPrefixState_UNDERRUN); + LONGBOW_RUN_TEST_CASE(Static, _parcSafeMemory_FormatPrefix); + LONGBOW_RUN_TEST_CASE(Static, _computeUsableMemoryLength); +} + +LONGBOW_TEST_FIXTURE_SETUP(Static) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Static) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Static, PARCSafeMemory_Report) +{ + size_t expectedSize = 100; + void *memory = parcSafeMemory_Allocate(expectedSize); + int fd = open("/dev/null", O_WRONLY); + _parcSafeMemory_Report(memory, fd); + close(fd); + parcSafeMemory_Deallocate(&memory); +} + +LONGBOW_TEST_CASE(Static, _parcSafeMemory_StateToString) +{ + assertNotNull(_parcSafeMemory_StateToString(PARCSafeMemoryState_OK), + "PARCSafeMemoryState_OK cannot be a NULL string."); + assertNotNull(_parcSafeMemory_StateToString(PARCSafeMemoryState_MISMATCHED), + "PARCSafeMemoryState_MISMATCHED cannot be a NULL string."); + assertNotNull(_parcSafeMemory_StateToString(PARCSafeMemoryState_UNDERRUN), + "PARCSafeMemoryState_UNDERRUN cannot be a NULL string."); + assertNotNull(_parcSafeMemory_StateToString(PARCSafeMemoryState_OVERRUN), + "PARCSafeMemoryState_OVERRUN cannot be a NULL string."); + assertNotNull(_parcSafeMemory_StateToString(PARCSafeMemoryState_NOTHINGALLOCATED), + "PARCSafeMemoryState_NOTHINGALLOCATED cannot be a NULL string."); + assertNotNull(_parcSafeMemory_StateToString(PARCSafeMemoryState_ALREADYFREE), + "PARCSafeMemoryState_ALREADYFREE cannot be a NULL string."); + assertNotNull(_parcSafeMemory_StateToString(-1), + "Garbage cannot be represented by a NULL string."); +} + +LONGBOW_TEST_CASE(Static, _parcSafeMemory_GetPrefixState_OK) +{ + size_t expectedLength = 5; + int expectedAlignment = sizeof(void *); + char origin[100]; // just some number + + void *memory = _parcSafeMemory_FormatPrefix((PARCSafeMemoryOrigin *) origin, expectedLength, expectedAlignment); + PARCSafeMemoryState actual = _parcSafeMemory_GetPrefixState(memory); + assertTrue(actual == PARCSafeMemoryState_OK, + "Expected PARCSafeMemoryState_OK, actual = %d", actual); +} + +LONGBOW_TEST_CASE(Static, _parcSafeMemory_GetPrefixState_ALREADYFREE) +{ + PARCSafeMemoryUsable *usable = parcSafeMemory_Allocate(10); + PARCSafeMemoryUsable *saved = usable; + + parcSafeMemory_Deallocate((void **) &usable); + + PARCSafeMemoryState actual = _parcSafeMemory_GetPrefixState(saved); + assertTrue(actual == PARCSafeMemoryState_ALREADYFREE, + "Expected PARCSafeMemoryState_ALREADYFREE, actual = %d", actual); +} + +LONGBOW_TEST_CASE(Static, _parcSafeMemory_GetPrefixState_UNDERRUN) +{ + char *usable = parcSafeMemory_Allocate(10); + + char savedByte = usable[-1]; + usable[-1] = 0; + + PARCSafeMemoryState actual = _parcSafeMemory_GetPrefixState((PARCSafeMemoryUsable *) usable); + assertTrue(actual == PARCSafeMemoryState_UNDERRUN, + "Expected PARCSafeMemoryState_UNDERRUN, actual = %d", actual); + usable[-1] = savedByte; + parcSafeMemory_Deallocate((void **) &usable); +} + +LONGBOW_TEST_CASE(Static, _parcSafeMemory_FormatPrefix) +{ + size_t expectedLength = 5; + int expectedAlignment = sizeof(void *) - 1; + char base[100]; // just some number + void *memory = _parcSafeMemory_FormatPrefix((PARCSafeMemoryOrigin *) base, expectedLength, expectedAlignment); + + assertNull(memory, + "Expected _parcSafeMemory_FormatPrefix to return NULL for bad alignment specification."); +} + +LONGBOW_TEST_CASE(Static, _computeUsableMemoryLength) +{ + size_t actual = _computeUsableMemoryLength(100, sizeof(void *)); + + // The result must be >= to the requested length and an even multiple of sizeof(void *) + assertTrue(actual >= 100 && (actual % sizeof(void *)) == 0, + "Expected the result to be >= to the requested length and an even multiple of sizeof(void *)"); +} + +LONGBOW_TEST_FIXTURE(ReportAllocation) +{ + LONGBOW_RUN_TEST_CASE(ReportAllocation, parcSafeMemory_ReportAllocation_Empty); + LONGBOW_RUN_TEST_CASE(ReportAllocation, parcSafeMemory_ReportAllocation_One); + LONGBOW_RUN_TEST_CASE(ReportAllocation, parcSafeMemory_ReportAllocation_Deallocated); +} + +LONGBOW_TEST_CASE(ReportAllocation, parcSafeMemory_ReportAllocation_Empty) +{ + _parcSafeMemory_DeallocateAll(); + int fd = open("/dev/null", O_WRONLY); + size_t result = parcSafeMemory_ReportAllocation(fd); + close(fd); + assertTrue(result == 0, "Expected 0, was %zd", result); +} + +LONGBOW_TEST_CASE(ReportAllocation, parcSafeMemory_ReportAllocation_One) +{ + void *memory; + size_t size = 100; + + memory = parcSafeMemory_Allocate(size); + + int fd = open("/dev/null", O_WRONLY); + size_t result = parcSafeMemory_ReportAllocation(fd); + close(fd); + assertTrue(result == 1, "Expected 1, was %zd", result); + + parcSafeMemory_Deallocate(&memory); +} + +LONGBOW_TEST_CASE(ReportAllocation, parcSafeMemory_ReportAllocation_Deallocated) +{ + size_t size = 100; + void *memory = parcSafeMemory_Allocate(size); + assertTrue(parcSafeMemory_Outstanding() != 0, "No memory allocated!"); + PARCSafeMemoryState state = _parcSafeMemory_GetState(memory); + parcSafeMemory_Deallocate(&memory); + assertTrue(state == PARCSafeMemoryState_OK, "Expected uncorrupted memory."); + + int fd = open("/dev/null", O_WRONLY); + size_t result = parcSafeMemory_ReportAllocation(fd); + close(fd); + + assertTrue(result == 0, "Expected 0, was %zd", result); +} + +LONGBOW_TEST_FIXTURE_SETUP(ReportAllocation) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(ReportAllocation) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcSafeMemory_Allocate); + LONGBOW_RUN_TEST_CASE(Global, parcSafeMemory_MemAlign); + + LONGBOW_RUN_TEST_CASE(Global, PARCSafeMemory_Realloc_Larger); + LONGBOW_RUN_TEST_CASE(Global, PARCSafeMemory_Realloc_Smaller); + LONGBOW_RUN_TEST_CASE(Global, parcSafeMemory_Reallocate_Zero); + LONGBOW_RUN_TEST_CASE(Global, PARCSafeMemory_Validate); + + LONGBOW_RUN_TEST_CASE(Global, parcSafeMemory_Allocate_BadAlignment); + LONGBOW_RUN_TEST_CASE(Global, parcSafeMemory_Allocate_BadSize); + LONGBOW_RUN_TEST_CASE(Global, parcSafeMemory_AllocateAndClear); + LONGBOW_RUN_TEST_CASE(Global, parcSafeMemory_Reallocate); + + LONGBOW_RUN_TEST_CASE(Global, parcSafeMemory_Deallocate_NothingAllocated); + + LONGBOW_RUN_TEST_CASE(Global, parcSafeMemory_IsValid_True); + LONGBOW_RUN_TEST_CASE(Global, parcSafeMemory_IsValid_False); + + LONGBOW_RUN_TEST_CASE(Global, parcSafeMemory_Display); + LONGBOW_RUN_TEST_CASE(Global, parcSafeMemory_Display_NULL); + + LONGBOW_RUN_TEST_CASE(Global, compute_prefix_length); + LONGBOW_RUN_TEST_CASE(Global, _parcSafeMemory_FormatMemory); + LONGBOW_RUN_TEST_CASE(Global, memory_prefix_format); + LONGBOW_RUN_TEST_CASE(Global, memory_prefix_validate); + LONGBOW_RUN_TEST_CASE(Global, memory_suffix_format); + LONGBOW_RUN_TEST_CASE(Global, memory_suffix_validate); + LONGBOW_RUN_TEST_CASE(Global, parcSafeMemory_StringDuplicate); + LONGBOW_RUN_TEST_CASE(Global, parcSafeMemory_StringDuplicate_Long); + LONGBOW_RUN_TEST_CASE(Global, parcSafeMemory_StringDuplicate_Short); + LONGBOW_RUN_TEST_CASE(Global, validateAlignment); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + assertTrue(parcSafeMemory_Outstanding() == 0, "Expected 0 outstanding allocations") + { + printf("Leaking test case: %s", longBowTestCase_GetName(testCase)); + } + return LONGBOW_STATUS_SUCCEEDED; +} + + +LONGBOW_TEST_CASE(Global, validateAlignment) +{ + assertTrue(_alignmentIsValid(sizeof(void *)), + "Expected alignment of sizeof(void *) failed."); + assertTrue(_alignmentIsValid(16), + "Expected alignment of 16 failed."); +} + +LONGBOW_TEST_CASE(Global, compute_prefix_length) +{ + // Test that the result is a multiple of the alignment value and greater than the size of _MemoryPrefix. + for (int i = 0; i < 9; i++) { + size_t alignment = 1 << i; + size_t actual = _computePrefixLength(alignment); + assertTrue((actual & (alignment - 1)) == 0, + "Alignment needs to be a multiple of %zd", alignment); + } +} + +LONGBOW_TEST_CASE(Global, memory_prefix_format) +{ + size_t expectedLength = 5; + int expectedAlignment = sizeof(void *); + char base[100]; + void *memory = _parcSafeMemory_FormatPrefix((PARCSafeMemoryOrigin *) base, expectedLength, expectedAlignment); + + _MemoryPrefix *prefix = _parcSafeMemory_GetPrefix(memory); + + assertAligned(prefix, sizeof(void *), + "prefix address %p is not aligned to %d", + memory, expectedAlignment); + assertAligned(memory, expectedAlignment, + "memory address %p is not aligned to %d", + memory, expectedAlignment); + + assertTrue((void *) prefix >= (void *) base, + "Expected >= %p, actual %p", (void *) base, (void *) prefix); + assertTrue(_parcSafeMemory_PrefixMagic == prefix->magic, + "Prefix magic is wrong."); + assertTrue(expectedLength == prefix->requestedLength, + "Expected length %zd, actual %zd", expectedLength, prefix->requestedLength); + assertTrue(expectedAlignment == prefix->alignment, + "Expected alignment %d, actual %zu", + expectedAlignment, prefix->alignment); + assertTrue(_parcSafeMemory_Guard == prefix->guard, + "Prefix guard is wrong."); +} + +LONGBOW_TEST_CASE(Global, memory_suffix_format) +{ + size_t expectedLength = 5; + int expectedAlignment = sizeof(void *); + char base[100]; + void *memory = _parcSafeMemory_FormatPrefix((PARCSafeMemoryOrigin *) base, expectedLength, expectedAlignment); + + _MemorySuffix *suffix = _parcSafeMemory_FormatSuffix(memory); + assertAligned(suffix, sizeof(void *), "suffix pointer is not aligned to %zu", sizeof(void*)); +} + +LONGBOW_TEST_CASE(Global, memory_suffix_validate) +{ + size_t expectedLength = 5; + int expectedAlignment = sizeof(void *); + char base[100]; + void *memory = _parcSafeMemory_FormatPrefix((PARCSafeMemoryOrigin *) base, expectedLength, expectedAlignment); + + _MemorySuffix *suffix = _parcSafeMemory_FormatSuffix(memory); + assertAligned(suffix, sizeof(void *), + "suffix pointer is not aligned to %zu", sizeof(void*)); + + PARCSafeMemoryState suffixState = _parcSafeMemory_GetSuffixState(memory); + assertTrue(suffixState == PARCSafeMemoryState_OK, + "Expected PARCSafeMemoryState_OK suffix state, actual %s", _parcSafeMemory_StateToString(suffixState)); +} + +LONGBOW_TEST_CASE(Global, memory_prefix_validate) +{ + size_t expectedLength = 5; + int expectedAlignment = sizeof(void *); + char base[100]; + + void *memory = _parcSafeMemory_FormatPrefix((PARCSafeMemoryOrigin *) base, expectedLength, expectedAlignment); + PARCSafeMemoryState actual = _parcSafeMemory_GetPrefixState(memory); + assertTrue(actual == PARCSafeMemoryState_OK, + "Expected valid prefix, actual = %d", actual); +} + +LONGBOW_TEST_CASE(Global, _parcSafeMemory_FormatMemory) +{ + size_t expectedLength = 5; + int expectedAlignment = sizeof(void *); + char base[100]; + + void *memory = _parcSafeMemory_FormatMemory((PARCSafeMemoryOrigin *) base, expectedLength, expectedAlignment); + + PARCSafeMemoryState state = _parcSafeMemory_GetState(memory); + + assertTrue(state == PARCSafeMemoryState_OK, + "Memory did not validate. Actual %d", state); +} + +LONGBOW_TEST_CASE(Global, parcSafeMemory_Allocate) +{ + void *memory; + size_t size = 100; + + memory = parcSafeMemory_Allocate(size); + + assertTrue(memory != NULL, + "Expected non-NULL return."); + + assertTrue((_parcSafeMemory_GetPrefixState(memory)) == PARCSafeMemoryState_OK, + "Prefix did not validate."); + parcSafeMemory_Deallocate(&memory); +} + +LONGBOW_TEST_CASE(Global, parcSafeMemory_MemAlign) +{ + void *memory; + size_t size = 100; + + int failure = parcSafeMemory_MemAlign(&memory, sizeof(void *), size); + assertTrue(failure == 0, + "parcSafeMemory_MemAlign failed: %d", failure); + + assertTrue(memory != NULL, + "Expected non-NULL return."); + + assertTrue((_parcSafeMemory_GetPrefixState(memory)) == PARCSafeMemoryState_OK, + "Prefix did not validate."); + parcSafeMemory_Deallocate(&memory); +} + +LONGBOW_TEST_CASE(Global, parcSafeMemory_ReportAllocation) +{ + void *memory; + size_t size = 100; + + memory = parcSafeMemory_Allocate(size); + assertTrue(memory != NULL, "Expected non-NULL return."); + PARCSafeMemoryState prefixState = _parcSafeMemory_GetPrefixState(memory); + assertTrue(prefixState == PARCSafeMemoryState_OK, + "Prefix did not validate."); + + parcSafeMemory_ReportAllocation(1); +} + +LONGBOW_TEST_CASE(Global, PARCSafeMemory_Validate) +{ + void *memory; + size_t size = 100; + + memory = parcSafeMemory_Allocate(size); + + assertTrue(_parcSafeMemory_GetState(memory) == PARCSafeMemoryState_OK, + "Memory did not validate."); + parcSafeMemory_Deallocate(&memory); +} + +LONGBOW_TEST_CASE(Global, PARCSafeMemory_Realloc_Larger) +{ + void *memory = parcSafeMemory_Allocate(100); + + for (unsigned char i = 0; i < 100; i++) { + ((unsigned char *) memory)[i] = i; + } + + assertTrue(_parcSafeMemory_GetState(memory) == PARCSafeMemoryState_OK, + "Expected memory to be OK."); + + size_t expectedLength = 100 + 1; + unsigned char *newMemory = parcSafeMemory_Reallocate(memory, expectedLength); + + assertTrue(_parcSafeMemory_GetState((PARCSafeMemoryUsable *) memory) != PARCSafeMemoryState_OK, + "Expected original memory to NOT be OK."); + assertTrue(_parcSafeMemory_GetState((PARCSafeMemoryUsable *) newMemory) == PARCSafeMemoryState_OK, + "Expected new memory to be OK."); + + _MemoryPrefix *prefix = _parcSafeMemory_GetPrefix((PARCSafeMemoryUsable *) newMemory); + assertTrue(prefix->requestedLength == expectedLength, + "Prefix Expected length %zd, actual %zd", expectedLength, prefix->requestedLength); + + for (int i = 0; i < 100; i++) { + assertTrue(((unsigned char *) newMemory)[i] == i, + "PARCSafeMemory_Realloc did not copy old memory correctly"); + } + + assertTrue(parcSafeMemory_Outstanding() != 0, + "No memory allocated!"); + PARCSafeMemoryState state = _parcSafeMemory_GetState((PARCSafeMemoryUsable *) newMemory); + parcSafeMemory_Deallocate((void **) &newMemory); + assertTrue(state == PARCSafeMemoryState_OK, + "Expected PARCSafeMemory_Deallocate of new memory to be OK, actual =%d", state); + assertTrue(_parcSafeMemory_GetState(memory) != PARCSafeMemoryState_OK, + "Expected old memory to be invalid."); +} + +LONGBOW_TEST_CASE(Global, PARCSafeMemory_Realloc_Smaller) +{ + void *memory = parcSafeMemory_Allocate(100); + assertTrue(_parcSafeMemory_GetState(memory) == PARCSafeMemoryState_OK, + "Memory did not validate."); + + for (unsigned char i = 0; i < 100; i++) { + ((unsigned char *) memory)[i] = i; + } + + size_t expectedLength = 100 - 1; + unsigned char *newMemory = parcSafeMemory_Reallocate(memory, expectedLength); + + assertTrue(_parcSafeMemory_GetState(memory) != PARCSafeMemoryState_OK, + "Expected original memory to NOT be OK."); + assertTrue(_parcSafeMemory_GetState((PARCSafeMemoryUsable *) newMemory) == PARCSafeMemoryState_OK, + "Expected new memory to be OK."); + + _MemoryPrefix *prefix = _parcSafeMemory_GetPrefix((PARCSafeMemoryUsable *) newMemory); + assertTrue(prefix->requestedLength == expectedLength, + "Prefix Expected length %zd, actual %zd", expectedLength, prefix->requestedLength); + + for (int i = 0; i < expectedLength; i++) { + assertTrue(((unsigned char *) newMemory)[i] == i, + "PARCSafeMemory_Realloc did not copy correctly"); + } + + assertTrue(parcSafeMemory_Outstanding() != 0, + "No memory allocated!"); + PARCSafeMemoryState state = _parcSafeMemory_GetState((PARCSafeMemoryUsable *) newMemory); + parcSafeMemory_Deallocate((void **) &newMemory); + assertTrue(state == PARCSafeMemoryState_OK, + "Expected PARCSafeMemory_Deallocate of new memory to be OK, actual =%d", state); + assertTrue(_parcSafeMemory_GetState(memory) != PARCSafeMemoryState_OK, + "Expected old memory to be invalid."); +} + +LONGBOW_TEST_CASE(Global, parcSafeMemory_Reallocate_Zero) +{ + void *memory = parcSafeMemory_Allocate(100); + assertTrue(_parcSafeMemory_GetState(memory) == PARCSafeMemoryState_OK, + "Memory did not validate."); + + for (unsigned char i = 0; i < 100; i++) { + ((unsigned char *) memory)[i] = i; + } + + size_t expectedLength = 0; + unsigned char *newMemory = parcSafeMemory_Reallocate(memory, expectedLength); + + assertTrue(newMemory == NULL, + "Expected NULL, actual %p", (void *) newMemory); + + parcSafeMemory_Deallocate(&memory); + assertNull(memory, + "Expected memory pointer to be NULL."); +} + +LONGBOW_TEST_CASE(Global, PARCSafeMemory_DoubleFree) +{ + size_t expectedSize = 100; + + void *memory = parcSafeMemory_Allocate(expectedSize); + assertTrue(_parcSafeMemory_GetState(memory) == PARCSafeMemoryState_OK, + "Memory did not validate."); + + for (unsigned char i = 0; i < expectedSize; i++) { + ((unsigned char *) memory)[i] = i; + } + + assertTrue(parcSafeMemory_Outstanding() != 0, + "No memory allocated!"); + PARCSafeMemoryState state = _parcSafeMemory_GetState(memory); + parcSafeMemory_Deallocate(&memory); + assertTrue(state == PARCSafeMemoryState_OK, + "Expected memory to validate"); + assertTrue(parcSafeMemory_Outstanding() != 0, + "No memory allocated!"); + state = _parcSafeMemory_GetState(memory); + parcSafeMemory_Deallocate(&memory); + assertTrue(state == PARCSafeMemoryState_UNDERRUN, + "Expected memory to be underrun (double free)."); +} + +LONGBOW_TEST_CASE(Global, parcSafeMemory_StringDuplicate) +{ + char *string = "hello world"; + char *actual = parcSafeMemory_StringDuplicate(string, strlen(string)); + + assertTrue(strcmp(string, actual) == 0, + "Expected %s, actual %s", string, actual); + parcSafeMemory_Deallocate((void **) &actual); +} + +LONGBOW_TEST_CASE(Global, parcSafeMemory_StringDuplicate_Long) +{ + char *string = "hello world"; + char *actual = parcSafeMemory_StringDuplicate(string, SIZE_MAX); + + assertTrue(strcmp(string, actual) == 0, + "Expected %s, actual %s", string, actual); + parcSafeMemory_Deallocate((void **) &actual); +} + +LONGBOW_TEST_CASE(Global, parcSafeMemory_StringDuplicate_Short) +{ + char *string = "hello world"; + char *expected = "hello"; + char *actual = parcSafeMemory_StringDuplicate(string, 5); + + assertTrue(strcmp(expected, actual) == 0, + "Expected %s, actual %s", expected, actual); + parcSafeMemory_Deallocate((void **) &actual); +} + +LONGBOW_TEST_CASE(Global, parcSafeMemory_Allocate_BadAlignment) +{ + void *result; + size_t alignment = 3; + size_t size = 100; + + int failure = parcSafeMemory_MemAlign(&result, alignment, size); + assertTrue(failure == EINVAL, + "parcSafeMemory_MemAlign failed to report bad aligment specification"); + assertTrue(parcSafeMemory_Outstanding() == 0, + "Expected 0 outstanding allocations, actual %d", parcSafeMemory_Outstanding()); +} + +LONGBOW_TEST_CASE(Global, parcSafeMemory_Allocate_BadSize) +{ + void *result; + size_t alignment = sizeof(void *); + size_t size = 0; + + int failure = parcSafeMemory_MemAlign(&result, alignment, size); + assertTrue(failure == EINVAL, + "parcSafeMemory_MemAlign failed to report bad aligment specification"); + assertTrue(parcSafeMemory_Outstanding() == 0, + "Expected 0 outstanding allocation, actual %d", parcSafeMemory_Outstanding()); +} + +LONGBOW_TEST_CASE(Global, parcSafeMemory_AllocateAndClear) +{ + void *result; + size_t size = 100; + + result = parcSafeMemory_AllocateAndClear(size); + assertNotNull(result, + "parcSafeMemory_AllocateAndClear failed: NULL result."); + + for (size_t i = 0; i < size; i++) { + assertTrue(((char *) result)[i] == 0, + "parcSafeMemory_AllocateAndClear failed to zero memory at index %zd", i); + } + assertTrue(parcSafeMemory_Outstanding() == 1, + "Expected 1 outstanding allocation, actual %d", parcSafeMemory_Outstanding()); + parcSafeMemory_Deallocate(&result); + assertTrue(parcSafeMemory_Outstanding() == 0, + "Expected 0 outstanding allocation, actual %d", parcSafeMemory_Outstanding()); +} + +LONGBOW_TEST_CASE(Global, parcSafeMemory_Reallocate) +{ + size_t size = 100; + + void *result = parcSafeMemory_AllocateAndClear(size); + assertNotNull(result, + "parcSafeMemory_Allocate failed: NULL."); + + for (size_t i = 0; i < size; i++) { + assertTrue(((char *) result)[i] == 0, + "parcSafeMemory_AllocateAndClear failed to zero memory at index %zd", i); + } + + result = parcSafeMemory_Reallocate(result, size * 2); + + assertTrue(parcSafeMemory_Outstanding() == 1, + "Expected 1 outstanding allocation, actual %d", parcSafeMemory_Outstanding()); + parcSafeMemory_Deallocate(&result); + assertTrue(parcSafeMemory_Outstanding() == 0, + "Expected 0 outstanding allocations, actual %d", parcSafeMemory_Outstanding()); +} + +LONGBOW_TEST_CASE(Global, parcSafeMemory_Deallocate_NothingAllocated) +{ + void *result = 0; + + parcSafeMemory_Deallocate(&result); +} + +LONGBOW_TEST_CASE(Global, parcSafeMemory_IsValid_True) +{ + void *result = parcSafeMemory_AllocateAndClear(5); + + assertTrue(parcSafeMemory_IsValid(result), "Expected properly allocated memory to be valid PARC Safe Memory."); + + parcSafeMemory_Deallocate(&result); +} + +LONGBOW_TEST_CASE(Global, parcSafeMemory_IsValid_False) +{ + char *memory[10]; + + assertFalse(parcSafeMemory_IsValid(memory), "Expected improperly allocated memory to be invalid PARC Safe Memory."); +} + +LONGBOW_TEST_CASE(Global, parcSafeMemory_Display) +{ + void *result = parcSafeMemory_AllocateAndClear(5); + + parcSafeMemory_Display(result, 0); + parcSafeMemory_Deallocate(&result); +} + +LONGBOW_TEST_CASE(Global, parcSafeMemory_Display_NULL) +{ + parcSafeMemory_Display(NULL, 0); +} + +LONGBOW_TEST_FIXTURE(Errors) +{ + LONGBOW_RUN_TEST_CASE(Errors, parcSafeMemory_Reallocate_NULL); + LONGBOW_RUN_TEST_CASE(Errors, PARCSafeMemory_Deallocate_Overrun); + LONGBOW_RUN_TEST_CASE(Errors, PARCSafeMemory_Deallocate_Underrun); +} + +LONGBOW_TEST_FIXTURE_SETUP(Errors) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Errors) +{ + // The tests purposefully wreck the various per-allocation accounting structures for the allocated memory in order to test for + // properly catching the overrun, underrun or other damage. + // As a result any cleanup of allocated memory is not possible, so these tests leak allocated memory. + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Errors, parcSafeMemory_Reallocate_NULL) +{ + void *result = NULL; + size_t size = 100; + + result = parcSafeMemory_Reallocate(result, size * 2); + + assertTrue(parcSafeMemory_Outstanding() == 1, + "Expected 1 outstanding allocation, actual %d", parcSafeMemory_Outstanding()); + parcSafeMemory_Deallocate(&result); + assertTrue(parcSafeMemory_Outstanding() == 0, + "Expected 0 outstanding allocations, actual %d", parcSafeMemory_Outstanding()); +} + +LONGBOW_TEST_CASE_EXPECTS(Errors, PARCSafeMemory_Deallocate_Underrun, .event = &LongBowTrapUnexpectedStateEvent) +{ + size_t expectedSize = sizeof(void *) * 2; + void *memory = parcSafeMemory_Allocate(expectedSize); + assertTrue(_parcSafeMemory_GetState(memory) == PARCSafeMemoryState_OK, + "Memory did not validate."); + + for (int i = -2; i < (int) expectedSize; i++) { + ((unsigned char *) memory)[i] = (unsigned char) i; + } + + assertTrue(_parcSafeMemory_GetState(memory) == PARCSafeMemoryState_UNDERRUN, + "Memory did not underrun."); + + assertTrue(parcSafeMemory_Outstanding() != 0, + "No memory allocated!"); + PARCSafeMemoryState state = _parcSafeMemory_GetState(memory); + parcSafeMemory_Deallocate(&memory); + + assertTrue(state == PARCSafeMemoryState_UNDERRUN, + "Expected memory to be underrun"); +} + +LONGBOW_TEST_CASE_EXPECTS(Errors, PARCSafeMemory_Deallocate_Overrun, .event = &LongBowTrapUnexpectedStateEvent) +{ + size_t expectedSize = 100; + void *memory = parcSafeMemory_Allocate(expectedSize); + assertTrue(_parcSafeMemory_GetState(memory) == PARCSafeMemoryState_OK, + "Memory did not validate."); + + for (unsigned char i = 0; i < expectedSize + 5; i++) { + ((unsigned char *) memory)[i] = i; + } + + assertTrue(parcSafeMemory_Outstanding() != 0, + "No memory allocated!"); + assertTrue(_parcSafeMemory_GetState(memory) != PARCSafeMemoryState_OK, + "Expected not OK, actual %s", _parcSafeMemory_StateToString(_parcSafeMemory_GetState(memory))); + // this is expected to fail + parcSafeMemory_Deallocate(&memory); +} + +LONGBOW_TEST_FIXTURE_OPTIONS(Performance, .enabled = false) +{ + LONGBOW_RUN_TEST_CASE(Performance, parcSafeMemory_AllocateDeallocate_1000000_WorstCase); + LONGBOW_RUN_TEST_CASE(Performance, parcSafeMemory_AllocateDeallocate_1000000_BestCase); + + LONGBOW_RUN_TEST_CASE(Performance, _computeUsableMemoryLength); +} + +LONGBOW_TEST_FIXTURE_SETUP(Performance) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Performance) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +void *memory[1000000]; + +LONGBOW_TEST_CASE(Performance, parcSafeMemory_AllocateDeallocate_1000000_WorstCase) +{ + size_t size = 100; + + for (int i = 0; i < sizeof(memory) / sizeof(memory[0]); i++) { + memory[i] = parcSafeMemory_Allocate(size); + } + for (int i = 0; i < sizeof(memory) / sizeof(memory[0]); i++) { + parcSafeMemory_Deallocate(&memory[i]); + } +} + +LONGBOW_TEST_CASE(Performance, parcSafeMemory_AllocateDeallocate_1000000_BestCase) +{ + size_t size = 100; + + for (int i = 0; i < sizeof(memory) / sizeof(memory[0]); i++) { + memory[i] = parcSafeMemory_Allocate(size); + } + + int i = sizeof(memory) / sizeof(memory[0]); + do { + i--; + parcSafeMemory_Deallocate(&memory[i]); + } while (i > 0); +} + +LONGBOW_TEST_CASE(Performance, _computeUsableMemoryLength) +{ + for (int i = 0; i < 100000000; i++) { + size_t alignment = sizeof(void *); + size_t requestedLength = 10; + _computeUsableMemoryLength(requestedLength, alignment); + } +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(safetyMemory); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_SortedList.c b/libparc/parc/algol/test/test_parc_SortedList.c new file mode 100644 index 00000000..f0445d86 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_SortedList.c @@ -0,0 +1,510 @@ +/* + * 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 "../parc_SortedList.c" + +#include <LongBow/testing.h> +#include <LongBow/debugging.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_SafeMemory.h> +#include <parc/algol/parc_DisplayIndented.h> + +#include <parc/testing/parc_MemoryTesting.h> +#include <parc/testing/parc_ObjectTesting.h> + +LONGBOW_TEST_RUNNER(parc_SortedList) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(CreateAcquireRelease); + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Specialization); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_SortedList) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_SortedList) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(CreateAcquireRelease) +{ + LONGBOW_RUN_TEST_CASE(CreateAcquireRelease, CreateRelease); + LONGBOW_RUN_TEST_CASE(CreateAcquireRelease, CreateCompare); +} + +LONGBOW_TEST_FIXTURE_SETUP(CreateAcquireRelease) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(CreateAcquireRelease) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(CreateAcquireRelease, CreateRelease) +{ + PARCSortedList *instance = parcSortedList_Create(); + assertNotNull(instance, "Expected non-null result from parcSortedList_Create();"); + + parcObjectTesting_AssertAcquire(instance); + + parcSortedList_Release(&instance); + assertNull(instance, "Expected null result from parcSortedList_Release();"); +} + +// A signum function to compare two PARCBuffers by length of buffer. +static int +_compareTwoBuffersByLength(const PARCObject *buf1, const PARCObject *buf2) +{ + size_t size1 = parcBuffer_Limit((PARCBuffer *) buf1); + size_t size2 = parcBuffer_Limit((PARCBuffer *) buf2); + + if (size1 > size2) { + return 1; + } else if (size2 > size1) { + return -1; + } + return 0; +} + +LONGBOW_TEST_CASE(CreateAcquireRelease, CreateCompare) +{ + PARCSortedList *instance = parcSortedList_CreateCompare(_compareTwoBuffersByLength); + + PARCBuffer *buf1 = parcBuffer_WrapCString("medium long"); + PARCBuffer *buf2 = parcBuffer_WrapCString("somewhat longer"); + PARCBuffer *buf3 = parcBuffer_WrapCString("short"); + + parcSortedList_Add(instance, buf1); + parcSortedList_Add(instance, buf2); + parcSortedList_Add(instance, buf3); + + PARCBuffer *test = parcSortedList_GetAtIndex(instance, 0); + assertTrue(test == buf3, "Expected the shortes buffer first"); + + test = parcSortedList_GetAtIndex(instance, 1); + assertTrue(test == buf1, "Expected the medium length buffer second"); + + test = parcSortedList_GetAtIndex(instance, 2); + assertTrue(test == buf2, "Expected the longest buffer last"); + + parcBuffer_Release(&buf1); + parcBuffer_Release(&buf2); + parcBuffer_Release(&buf3); + + parcSortedList_Release(&instance); +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcSortedList_Copy); + LONGBOW_RUN_TEST_CASE(Global, parcSortedList_Display); + LONGBOW_RUN_TEST_CASE(Global, parcSortedList_Equals); + LONGBOW_RUN_TEST_CASE(Global, parcSortedList_HashCode); + LONGBOW_RUN_TEST_CASE(Global, parcSortedList_IsValid); + LONGBOW_RUN_TEST_CASE(Global, parcSortedList_ToJSON); + LONGBOW_RUN_TEST_CASE(Global, parcSortedList_ToString); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s mismanaged memory.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcSortedList_Copy) +{ + PARCSortedList *instance = parcSortedList_Create(); + PARCSortedList *copy = parcSortedList_Copy(instance); + assertTrue(parcSortedList_Equals(instance, copy), "Expected the copy to be equal to the original"); + + parcSortedList_Release(&instance); + parcSortedList_Release(©); +} + +LONGBOW_TEST_CASE(Global, parcSortedList_Display) +{ + PARCSortedList *instance = parcSortedList_Create(); + parcSortedList_Display(instance, 0); + parcSortedList_Release(&instance); +} + +LONGBOW_TEST_CASE(Global, parcSortedList_Equals) +{ + PARCSortedList *x = parcSortedList_Create(); + PARCSortedList *y = parcSortedList_Create(); + PARCSortedList *z = parcSortedList_Create(); + + parcObjectTesting_AssertEquals(x, y, z, NULL); + + parcSortedList_Release(&x); + parcSortedList_Release(&y); + parcSortedList_Release(&z); +} + +LONGBOW_TEST_CASE(Global, parcSortedList_HashCode) +{ + PARCSortedList *x = parcSortedList_Create(); + PARCSortedList *y = parcSortedList_Create(); + + parcObjectTesting_AssertHashCode(x, y); + + parcSortedList_Release(&x); + parcSortedList_Release(&y); +} + +LONGBOW_TEST_CASE(Global, parcSortedList_IsValid) +{ + PARCSortedList *instance = parcSortedList_Create(); + assertTrue(parcSortedList_IsValid(instance), "Expected parcSortedList_Create to result in a valid instance."); + + parcSortedList_Release(&instance); + assertFalse(parcSortedList_IsValid(instance), "Expected parcSortedList_Release to result in an invalid instance."); +} + +LONGBOW_TEST_CASE(Global, parcSortedList_ToJSON) +{ + PARCSortedList *instance = parcSortedList_Create(); + + PARCJSON *json = parcSortedList_ToJSON(instance); + + parcJSON_Release(&json); + + parcSortedList_Release(&instance); +} + +LONGBOW_TEST_CASE(Global, parcSortedList_ToString) +{ + PARCSortedList *instance = parcSortedList_Create(); + + char *string = parcSortedList_ToString(instance); + + assertNotNull(string, "Expected non-NULL result from parcSortedList_ToString"); + + parcMemory_Deallocate((void **) &string); + parcSortedList_Release(&instance); +} + +LONGBOW_TEST_FIXTURE(Specialization) +{ + LONGBOW_RUN_TEST_CASE(Specialization, parcSortedList_Add); + LONGBOW_RUN_TEST_CASE(Specialization, parcSortedList_Remove); + LONGBOW_RUN_TEST_CASE(Specialization, parcSortedList_GetAtIndex); + LONGBOW_RUN_TEST_CASE(Specialization, parcSortedList_GetFirst); + LONGBOW_RUN_TEST_CASE(Specialization, parcSortedList_GetLast); + + LONGBOW_RUN_TEST_CASE(Specialization, parcSortedList_RemoveFirst); + LONGBOW_RUN_TEST_CASE(Specialization, parcSortedList_RemoveFirst_SingleElement); + LONGBOW_RUN_TEST_CASE(Specialization, parcSortedList_RemoveLast); +} + +LONGBOW_TEST_FIXTURE_SETUP(Specialization) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Specialization) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s mismanaged memory.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +static void +dump(PARCSortedList *i) +{ + PARCIterator *iterator = parcSortedList_CreateIterator(i); + while (parcIterator_HasNext(iterator)) { + PARCBuffer *buffer = parcIterator_Next(iterator); + parcBuffer_Display(buffer, 0); + } + + parcIterator_Release(&iterator); +} + +LONGBOW_TEST_CASE(Specialization, parcSortedList_Add) +{ + PARCSortedList *instance = parcSortedList_Create(); + PARCBuffer *element1 = parcBuffer_WrapCString("1"); + PARCBuffer *element2 = parcBuffer_WrapCString("2"); + PARCBuffer *element3 = parcBuffer_WrapCString("3"); + PARCBuffer *element4 = parcBuffer_WrapCString("4"); + PARCBuffer *element7 = parcBuffer_WrapCString("7"); + PARCBuffer *element6 = parcBuffer_WrapCString("6"); + PARCBuffer *element5 = parcBuffer_WrapCString("5"); + PARCBuffer *element8 = parcBuffer_WrapCString("8"); + + parcSortedList_Add(instance, element2); + parcSortedList_Display(instance, 0); + parcSortedList_Add(instance, element8); + parcSortedList_Display(instance, 0); + parcSortedList_Add(instance, element3); + parcSortedList_Display(instance, 0); + parcSortedList_Add(instance, element4); + parcSortedList_Display(instance, 0); + parcSortedList_Add(instance, element7); + parcSortedList_Display(instance, 0); + parcSortedList_Add(instance, element6); + parcSortedList_Display(instance, 0); + parcSortedList_Add(instance, element5); + parcSortedList_Display(instance, 0); + parcSortedList_Add(instance, element1); + parcSortedList_Display(instance, 0); + parcSortedList_Add(instance, element6); + parcSortedList_Display(instance, 0); + + dump(instance); + + parcBuffer_Release(&element1); + parcBuffer_Release(&element2); + parcBuffer_Release(&element3); + parcBuffer_Release(&element4); + parcBuffer_Release(&element5); + parcBuffer_Release(&element6); + parcBuffer_Release(&element7); + parcBuffer_Release(&element8); + parcSortedList_Release(&instance); +} + +LONGBOW_TEST_CASE(Specialization, parcSortedList_Remove) +{ + PARCSortedList *instance = parcSortedList_Create(); + PARCBuffer *element1 = parcBuffer_WrapCString("1"); + PARCBuffer *element2 = parcBuffer_WrapCString("2"); + PARCBuffer *element3 = parcBuffer_WrapCString("3"); + PARCBuffer *element4 = parcBuffer_WrapCString("4"); + PARCBuffer *element7 = parcBuffer_WrapCString("7"); + PARCBuffer *element6 = parcBuffer_WrapCString("6"); + PARCBuffer *element5 = parcBuffer_WrapCString("5"); + PARCBuffer *element8 = parcBuffer_WrapCString("8"); + + parcSortedList_Add(instance, element1); + parcSortedList_Add(instance, element2); + parcSortedList_Add(instance, element3); + parcSortedList_Display(instance, 0); + + parcSortedList_Remove(instance, element2); + + assertTrue(parcSortedList_Size(instance) == 2, "Expected list to be 2 in size"); + + parcBuffer_Release(&element1); + parcBuffer_Release(&element2); + parcBuffer_Release(&element3); + parcBuffer_Release(&element4); + parcBuffer_Release(&element5); + parcBuffer_Release(&element6); + parcBuffer_Release(&element7); + parcBuffer_Release(&element8); + parcSortedList_Release(&instance); +} + +LONGBOW_TEST_CASE(Specialization, parcSortedList_GetAtIndex) +{ + PARCSortedList *instance = parcSortedList_Create(); + PARCBuffer *element1 = parcBuffer_WrapCString("1"); + PARCBuffer *element2 = parcBuffer_WrapCString("2"); + PARCBuffer *element3 = parcBuffer_WrapCString("3"); + PARCBuffer *element4 = parcBuffer_WrapCString("4"); + PARCBuffer *element7 = parcBuffer_WrapCString("7"); + PARCBuffer *element6 = parcBuffer_WrapCString("6"); + PARCBuffer *element5 = parcBuffer_WrapCString("5"); + PARCBuffer *element8 = parcBuffer_WrapCString("8"); + + parcSortedList_Add(instance, element1); + parcSortedList_Add(instance, element2); + parcSortedList_Add(instance, element3); + + PARCBuffer *actual = (PARCBuffer *) parcSortedList_GetAtIndex(instance, 1); + assertTrue(parcBuffer_Equals(element2, actual), "Got the wrong value at index 1"); + + parcBuffer_Release(&element1); + parcBuffer_Release(&element2); + parcBuffer_Release(&element3); + parcBuffer_Release(&element4); + parcBuffer_Release(&element5); + parcBuffer_Release(&element6); + parcBuffer_Release(&element7); + parcBuffer_Release(&element8); + parcSortedList_Release(&instance); +} + +LONGBOW_TEST_CASE(Specialization, parcSortedList_GetFirst) +{ + PARCSortedList *instance = parcSortedList_Create(); + PARCBuffer *element1 = parcBuffer_WrapCString("1"); + PARCBuffer *element2 = parcBuffer_WrapCString("2"); + PARCBuffer *element3 = parcBuffer_WrapCString("3"); + PARCBuffer *element4 = parcBuffer_WrapCString("4"); + PARCBuffer *element7 = parcBuffer_WrapCString("7"); + PARCBuffer *element6 = parcBuffer_WrapCString("6"); + PARCBuffer *element5 = parcBuffer_WrapCString("5"); + PARCBuffer *element8 = parcBuffer_WrapCString("8"); + + parcSortedList_Add(instance, element1); + parcSortedList_Add(instance, element2); + parcSortedList_Add(instance, element3); + + PARCBuffer *actual = (PARCBuffer *) parcSortedList_GetFirst(instance); + assertTrue(parcBuffer_Equals(element1, actual), "Got the wrong value."); + + parcBuffer_Release(&element1); + parcBuffer_Release(&element2); + parcBuffer_Release(&element3); + parcBuffer_Release(&element4); + parcBuffer_Release(&element5); + parcBuffer_Release(&element6); + parcBuffer_Release(&element7); + parcBuffer_Release(&element8); + parcSortedList_Release(&instance); +} + +LONGBOW_TEST_CASE(Specialization, parcSortedList_GetLast) +{ + PARCSortedList *instance = parcSortedList_Create(); + PARCBuffer *element1 = parcBuffer_WrapCString("1"); + PARCBuffer *element2 = parcBuffer_WrapCString("2"); + PARCBuffer *element3 = parcBuffer_WrapCString("3"); + PARCBuffer *element4 = parcBuffer_WrapCString("4"); + PARCBuffer *element7 = parcBuffer_WrapCString("7"); + PARCBuffer *element6 = parcBuffer_WrapCString("6"); + PARCBuffer *element5 = parcBuffer_WrapCString("5"); + PARCBuffer *element8 = parcBuffer_WrapCString("8"); + + parcSortedList_Add(instance, element1); + parcSortedList_Add(instance, element2); + parcSortedList_Add(instance, element3); + + PARCBuffer *actual = (PARCBuffer *) parcSortedList_GetLast(instance); + assertTrue(parcBuffer_Equals(element3, actual), "Got the wrong value at index 1"); + + parcBuffer_Release(&element1); + parcBuffer_Release(&element2); + parcBuffer_Release(&element3); + parcBuffer_Release(&element4); + parcBuffer_Release(&element5); + parcBuffer_Release(&element6); + parcBuffer_Release(&element7); + parcBuffer_Release(&element8); + parcSortedList_Release(&instance); +} + +LONGBOW_TEST_CASE(Specialization, parcSortedList_RemoveFirst) +{ + PARCSortedList *deque = parcSortedList_Create(); + + PARCBuffer *object1 = parcBuffer_WrapCString("1"); + PARCBuffer *object2 = parcBuffer_WrapCString("2"); + PARCBuffer *object3 = parcBuffer_WrapCString("3"); + + parcSortedList_Add(deque, object1); + parcSortedList_Add(deque, object2); + parcSortedList_Add(deque, object3); + + PARCBuffer *peek = parcSortedList_RemoveFirst(deque); + assertTrue(parcBuffer_Equals(object1, peek), "Objects out of order"); + + parcBuffer_Release(&peek); + parcBuffer_Release(&object1); + parcBuffer_Release(&object2); + parcBuffer_Release(&object3); + parcSortedList_Release(&deque); +} + +LONGBOW_TEST_CASE(Specialization, parcSortedList_RemoveFirst_SingleElement) +{ + PARCBuffer *object1 = parcBuffer_WrapCString("1"); + PARCSortedList *deque = parcSortedList_Create(); + parcSortedList_Add(deque, object1); + + PARCBuffer *peek = parcSortedList_RemoveFirst(deque); + assertTrue(parcBuffer_Equals(object1, peek), + "Objects out of order."); + + parcBuffer_Release(&peek); + parcBuffer_Release(&object1); + parcSortedList_Release(&deque); +} + +LONGBOW_TEST_CASE(Specialization, parcSortedList_RemoveLast) +{ + PARCBuffer *object1 = parcBuffer_WrapCString("1"); + PARCBuffer *object2 = parcBuffer_WrapCString("2"); + PARCBuffer *object3 = parcBuffer_WrapCString("3"); + + PARCSortedList *deque = parcSortedList_Create(); + parcSortedList_Add(deque, object1); + parcSortedList_Add(deque, object2); + parcSortedList_Add(deque, object3); + + PARCBuffer *peek = parcSortedList_RemoveLast(deque); + assertTrue(parcBuffer_Equals(object3, peek), + "Objects out of order."); + + parcBuffer_Release(&peek); + + parcBuffer_Release(&object1); + parcBuffer_Release(&object2); + parcBuffer_Release(&object3); + parcSortedList_Release(&deque); +} + +LONGBOW_TEST_CASE(Specialization, parcSortedList_RemoveLast_SingleElement) +{ + PARCBuffer *object1 = parcBuffer_WrapCString("1"); + + PARCSortedList *deque = parcSortedList_Create(); + parcSortedList_Add(deque, object1); + + PARCBuffer *peek = parcSortedList_RemoveLast(deque); + assertTrue(parcBuffer_Equals(object1, peek), + "Objects out of order."); + + parcBuffer_Release(&object1); + parcSortedList_Release(&deque); +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_SortedList); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_Stack.c b/libparc/parc/algol/test/test_parc_Stack.c new file mode 100755 index 00000000..3ca27682 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_Stack.c @@ -0,0 +1,119 @@ +/* + * 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. + */ + +/** + * @header <#Headline Name#> + * @abstract <#Abstract#> + * @discussion + * <#Discussion#> + * + */ +#include "../parc_Stack.c" + +#include <LongBow/unit-test.h> +#include <LongBow/debugging.h> + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. + +#include <parc/algol/parc_ArrayList.h> +#include <parc/algol/parc_Deque.h> +#include <parc/algol/parc_SafeMemory.h> + +LONGBOW_TEST_RUNNER(test_parc_Stack) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(test_parc_Stack) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(test_parc_Stack) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcStack_IsEmpty_PARCDeque); + LONGBOW_RUN_TEST_CASE(Global, parcStack_IsEmpty_PARCArrayList); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcStack_IsEmpty_PARCDeque) +{ + PARCStackInterface dequeAsStack = { + .parcStack_Release = (void (*)(void **))parcDeque_Release, + .parcStack_IsEmpty = (bool (*)(const void *))parcDeque_IsEmpty, + .parcStack_Peek = (void *(*)(const void *))parcDeque_PeekLast, + .parcStack_Pop = (void *(*)(void *))parcDeque_RemoveLast, + .parcStack_Push = (void *(*)(void *, void *))parcDeque_Append, + .parcStack_Search = NULL + }; + + PARCStack *stack = parcStack(parcDeque_Create(), &dequeAsStack); + + bool actual = parcStack_IsEmpty(stack); + parcStack_Release(&stack); + assertTrue(actual, "Expected the stack to be empty."); +} + +LONGBOW_TEST_CASE(Global, parcStack_IsEmpty_PARCArrayList) +{ + PARCStackInterface arrayListAsStack = { + .parcStack_Release = (void (*)(void **))parcArrayList_Destroy, + .parcStack_IsEmpty = (bool (*)(const void *))parcArrayList_IsEmpty, + .parcStack_Peek = (void *(*)(const void *))parcArrayList_Peek, + .parcStack_Pop = (void *(*)(void *))parcArrayList_Pop, + .parcStack_Push = (void *(*)(void *, void *))parcArrayList_Add, + .parcStack_Search = NULL + }; + + PARCStack *stack = parcStack(parcArrayList_Create(NULL), &arrayListAsStack); + + bool actual = parcStack_IsEmpty(stack); + parcStack_Release(&stack); + assertTrue(actual, "Expected the stack to be empty."); +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(test_parc_Stack); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_StdlibMemory.c b/libparc/parc/algol/test/test_parc_StdlibMemory.c new file mode 100644 index 00000000..7cf49991 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_StdlibMemory.c @@ -0,0 +1,326 @@ +/* + * 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 "../parc_StdlibMemory.c" + +#include <LongBow/testing.h> +#include <LongBow/debugging.h> + +#include <parc/testing/parc_MemoryTesting.h> + +LONGBOW_TEST_RUNNER(test_parc_StdlibMemory) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Threads); + LONGBOW_RUN_TEST_FIXTURE(Performance); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(test_parc_StdlibMemory) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(test_parc_StdlibMemory) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcStdlibMemory_Allocate); + LONGBOW_RUN_TEST_CASE(Global, parcStdlibMemory_MemAlign_BadAlignment); + LONGBOW_RUN_TEST_CASE(Global, parcStdlibMemory_MemAlign_BadSize); + LONGBOW_RUN_TEST_CASE(Global, parcStdlibMemory_AllocateAndClear); + LONGBOW_RUN_TEST_CASE(Global, parcStdlibMemory_AllocateAndClear_BadSize); + LONGBOW_RUN_TEST_CASE(Global, parcStdlibMemory_Reallocate); + LONGBOW_RUN_TEST_CASE(Global, parcStdlibMemory_Reallocate_NULL); + LONGBOW_RUN_TEST_CASE(Global, parcStdlibMemory_StringDuplicate); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s leaks allocations.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcStdlibMemory_Allocate) +{ + size_t size = 100; + + void *result = parcStdlibMemory_Allocate(size); + + assertNotNull(result, "parcStdlibMemory_Allocate failed: NULL result."); + assertTrue(parcStdlibMemory_Outstanding() == 1, + "Expected 1 outstanding allocation, actual %d", parcStdlibMemory_Outstanding()); + parcStdlibMemory_Deallocate(&result); +} + +LONGBOW_TEST_CASE(Global, parcStdlibMemory_MemAlign_BadAlignment) +{ + void *result; + size_t alignment = 3; + size_t size = 1200; + + int failure = parcStdlibMemory_MemAlign(&result, alignment, size); + assertTrue(failure == EINVAL, + "parcStdlibMemory_Allocate failed to report bad aligment specification"); + assertTrue(parcStdlibMemory_Outstanding() == 0, + "Expected 0 outstanding allocations, actual %d", parcStdlibMemory_Outstanding()); +} + +LONGBOW_TEST_CASE(Global, parcStdlibMemory_MemAlign_BadSize) +{ + void *result; + size_t alignment = sizeof(void *); + size_t size = 0; + + int failure = parcStdlibMemory_MemAlign(&result, alignment, size); + assertTrue(failure == EINVAL, + "parcStdlibMemory_Allocate failed to report bad aligment specification"); + assertTrue(parcStdlibMemory_Outstanding() == 0, + "Expected 0 outstanding allocation, actual %d", parcStdlibMemory_Outstanding()); +} + +LONGBOW_TEST_CASE(Global, parcStdlibMemory_AllocateAndClear) +{ + size_t size = 1200; + + void *result = parcStdlibMemory_AllocateAndClear(size); + assertNotNull(result, "parcStdlibMemory_Allocate failed: NULL result."); + + for (size_t i = 0; i < size; i++) { + assertTrue(((char *) result)[i] == 0, + "parcStdlibMemory_AllocateAndClear failed to zero memory at index %zd", i); + } + assertTrue(parcStdlibMemory_Outstanding() == 1, + "Expected 1 outstanding allocation, actual %d", parcStdlibMemory_Outstanding()); + parcStdlibMemory_Deallocate(&result); + assertTrue(parcStdlibMemory_Outstanding() == 0, + "Expected 0 outstanding allocation, actual %d", parcStdlibMemory_Outstanding()); +} + +LONGBOW_TEST_CASE(Global, parcStdlibMemory_AllocateAndClear_BadSize) +{ + void *result; + size_t alignment = sizeof(void *); + size_t size = 0; + + int failure = parcStdlibMemory_MemAlign(&result, alignment, size); + assertTrue(failure == EINVAL, + "parcStdlibMemory_Allocate failed to report bad aligment specification"); + assertTrue(parcStdlibMemory_Outstanding() == 0, + "Expected 0 outstanding allocations, actual %d", parcStdlibMemory_Outstanding()); +} + +static void +_test_SetMemory(unsigned char *memory, size_t size) +{ + for (size_t i = 0; i < size; i++) { + memory[i] = i; + } +} + +static void +_test_CheckMemory(unsigned char *memory, size_t size) +{ + for (size_t i = 0; i < size; i++) { + assertTrue(memory[i] == (i % 256), "memory failed to check at index %zd", i); + } +} + + +LONGBOW_TEST_CASE(Global, parcStdlibMemory_Reallocate) +{ + size_t size = 1200; + + void *result = parcStdlibMemory_AllocateAndClear(size); + assertNotNull(result, + "parcStdlibMemory_Allocate failed: NULL result."); + + _test_SetMemory(result, size); + _test_CheckMemory(result, size); + + result = parcStdlibMemory_Reallocate(result, size * 2); + + _test_CheckMemory(result, size); + + assertTrue(parcStdlibMemory_Outstanding() == 1, + "Expected 1 outstanding allocation, actual %d", parcStdlibMemory_Outstanding()); + parcStdlibMemory_Deallocate(&result); + assertTrue(parcStdlibMemory_Outstanding() == 0, + "Expected 0 outstanding allocations, actual %d", parcStdlibMemory_Outstanding()); +} + +LONGBOW_TEST_CASE(Global, parcStdlibMemory_Reallocate_NULL) +{ + void *result = NULL; + size_t size = 1200; + + result = parcStdlibMemory_Reallocate(result, size * 2); + _test_SetMemory(result, size * 2); + _test_CheckMemory(result, size * 2); + + assertTrue(parcStdlibMemory_Outstanding() == 1, + "Expected 1 outstanding allocation, actual %d", parcStdlibMemory_Outstanding()); + parcStdlibMemory_Deallocate(&result); + assertTrue(parcStdlibMemory_Outstanding() == 0, + "Expected 0 outstanding allocations, actual %d", parcStdlibMemory_Outstanding()); +} + +LONGBOW_TEST_CASE(Global, parcStdlibMemory_StringDuplicate) +{ + char *expected = "Hello World"; + char *actual = parcStdlibMemory_StringDuplicate(expected, strlen(expected)); + + assertTrue(expected != actual, + "Expected a distinct pointer unequal to the original string"); + assertTrue(strcmp(expected, actual) == 0, + "Expected strings to be equal. '%s' vs '%s'", expected, actual); + + assertTrue(parcStdlibMemory_Outstanding() == 1, + "Expected 1 outstanding allocation, actual %d", parcStdlibMemory_Outstanding()); + parcStdlibMemory_Deallocate((void **) &actual); + assertTrue(parcStdlibMemory_Outstanding() == 0, + "Expected 0 outstanding allocations, actual %d", parcStdlibMemory_Outstanding()); +} + +LONGBOW_TEST_FIXTURE(Threads) +{ + LONGBOW_RUN_TEST_CASE(Threads, Threads1000); +} + +LONGBOW_TEST_FIXTURE_SETUP(Threads) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Threads) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +void * +allocator(void *unused) +{ + for (int i = 0; i < 1000; i++) { + void *memory = parcStdlibMemory_Allocate(10); + parcStdlibMemory_Deallocate(&memory); + } + return 0; +} + +LONGBOW_TEST_CASE(Threads, Threads1000) +{ +#define NTHREADS 1000 + pthread_t thread[NTHREADS]; + + for (int i = 0; i < NTHREADS; i++) { + pthread_create(&thread[i], NULL, allocator, NULL); + } + for (int i = 0; i < NTHREADS; i++) { + pthread_join(thread[0], NULL); + } +} + +LONGBOW_TEST_FIXTURE_OPTIONS(Performance, .enabled = false) +{ + LONGBOW_RUN_TEST_CASE(Performance, parcStdlibMemory_AllocateDeallocate_Forward); + LONGBOW_RUN_TEST_CASE(Performance, parcStdlibMemory_AllocateDeallocate_Reverse); + LONGBOW_RUN_TEST_CASE(Performance, parcStdlibMemory_MemAlignDeallocate_Forward); + LONGBOW_RUN_TEST_CASE(Performance, parcStdlibMemory_MemAlignDeallocate_Reverse); +} + +LONGBOW_TEST_FIXTURE_SETUP(Performance) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Performance) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +#define ELEMENT_COUNT 1000000 +#define ELEMENT_SIZE 151 +void *memory[ELEMENT_COUNT]; + +LONGBOW_TEST_CASE(Performance, parcStdlibMemory_AllocateDeallocate_Forward) +{ + for (int i = 0; i < ELEMENT_COUNT; i++) { + memory[i] = parcStdlibMemory_Allocate(ELEMENT_SIZE); + } + for (int i = 0; i < ELEMENT_COUNT; i++) { + parcStdlibMemory_Deallocate(&memory[i]); + } +} + +LONGBOW_TEST_CASE(Performance, parcStdlibMemory_AllocateDeallocate_Reverse) +{ + for (int i = 0; i < ELEMENT_COUNT; i++) { + memory[i] = parcStdlibMemory_Allocate(ELEMENT_SIZE); + } + + int i = ELEMENT_COUNT; + do { + i--; + parcStdlibMemory_Deallocate(&memory[i]); + } while (i > 0); +} + +LONGBOW_TEST_CASE(Performance, parcStdlibMemory_MemAlignDeallocate_Forward) +{ + for (int i = 0; i < ELEMENT_COUNT; i++) { + parcStdlibMemory_MemAlign(&memory[i], sizeof(void *), ELEMENT_SIZE); + } + for (int i = 0; i < ELEMENT_COUNT; i++) { + parcStdlibMemory_Deallocate(&memory[i]); + } +} + +LONGBOW_TEST_CASE(Performance, parcStdlibMemory_MemAlignDeallocate_Reverse) +{ + for (int i = 0; i < ELEMENT_COUNT; i++) { + parcStdlibMemory_MemAlign(&memory[i], sizeof(void *), ELEMENT_SIZE); + } + + int i = ELEMENT_COUNT; + do { + i--; + parcStdlibMemory_Deallocate(&memory[i]); + } while (i > 0); +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(test_parc_StdlibMemory); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_String.c b/libparc/parc/algol/test/test_parc_String.c new file mode 100644 index 00000000..84d3d3da --- /dev/null +++ b/libparc/parc/algol/test/test_parc_String.c @@ -0,0 +1,210 @@ +/* + * 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 "../parc_String.c" + +#include <LongBow/testing.h> +#include <LongBow/debugging.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_SafeMemory.h> +#include <parc/algol/parc_DisplayIndented.h> + +#include <parc/testing/parc_MemoryTesting.h> +#include <parc/testing/parc_ObjectTesting.h> + +LONGBOW_TEST_RUNNER(parc_String) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(CreateAcquireRelease); + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_String) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_String) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(CreateAcquireRelease) +{ + LONGBOW_RUN_TEST_CASE(CreateAcquireRelease, CreateRelease); +} + +LONGBOW_TEST_FIXTURE_SETUP(CreateAcquireRelease) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(CreateAcquireRelease) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(CreateAcquireRelease, CreateRelease) +{ + PARCString *instance = parcString_Create("Hello World"); + assertNotNull(instance, "Expected non-null result from parcString_Create();"); + parcObjectTesting_AssertAcquireReleaseImpl(instance); + + parcString_Release(&instance); + assertNull(instance, "Expected null result from parcString_Release();"); +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcString_Compare); + LONGBOW_RUN_TEST_CASE(Global, parcString_Copy); + LONGBOW_RUN_TEST_CASE(Global, parcString_Display); + LONGBOW_RUN_TEST_CASE(Global, parcString_Equals); + LONGBOW_RUN_TEST_CASE(Global, parcString_HashCode); + LONGBOW_RUN_TEST_CASE(Global, parcString_IsValid); + LONGBOW_RUN_TEST_CASE(Global, parcString_ToJSON); + LONGBOW_RUN_TEST_CASE(Global, parcString_ToString); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s mismanaged memory.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcString_Compare) +{ + PARCString *string = parcString_Create("Hello1"); + PARCString *equivalent[2] = { + parcString_Create("Hello1"), + NULL + }; + PARCString *greater[2] = { + parcString_Create("Hello2"), + NULL + }; + PARCString *lesser[2] = { + parcString_Create("Hello0"), + NULL + }; + + parcObjectTesting_AssertCompareTo(parcString_Compare, string, equivalent, lesser, greater); + parcString_Release(&string); + parcString_Release(&equivalent[0]); + parcString_Release(&greater[0]); + parcString_Release(&lesser[0]); +} + +LONGBOW_TEST_CASE(Global, parcString_Copy) +{ + PARCString *instance = parcString_Create("Hello World"); + PARCString *copy = parcString_Copy(instance); + assertTrue(parcString_Equals(instance, copy), "Expected the copy to be equal to the original"); + + parcString_Release(&instance); + parcString_Release(©); +} + +LONGBOW_TEST_CASE(Global, parcString_Display) +{ + PARCString *instance = parcString_Create("Hello World"); + parcString_Display(instance, 0); + parcString_Release(&instance); +} + +LONGBOW_TEST_CASE(Global, parcString_Equals) +{ + PARCString *x = parcString_Create("Hello World"); + PARCString *y = parcString_Create("Hello World"); + PARCString *z = parcString_Create("Hello World"); + + parcObjectTesting_AssertEquals(x, y, z, NULL); + + parcString_Release(&x); + parcString_Release(&y); + parcString_Release(&z); +} + +LONGBOW_TEST_CASE(Global, parcString_HashCode) +{ + PARCString *x = parcString_Create("Hello World"); + PARCString *y = parcString_Create("Hello World"); + + parcObjectTesting_AssertHashCode(x, y); + + parcString_Release(&x); + parcString_Release(&y); +} + +LONGBOW_TEST_CASE(Global, parcString_IsValid) +{ + PARCString *instance = parcString_Create("Hello World"); + assertTrue(parcString_IsValid(instance), "Expected parcString_Create to result in a valid instance."); + + parcString_Release(&instance); + assertFalse(parcString_IsValid(instance), "Expected parcString_Release to result in an invalid instance."); +} + +LONGBOW_TEST_CASE(Global, parcString_ToJSON) +{ + PARCString *instance = parcString_Create("Hello World"); + + PARCJSON *json = parcString_ToJSON(instance); + + parcJSON_Release(&json); + + parcString_Release(&instance); +} + +LONGBOW_TEST_CASE(Global, parcString_ToString) +{ + PARCString *instance = parcString_Create("Hello World"); + + char *string = parcString_ToString(instance); + + assertNotNull(string, "Expected non-NULL result from parcString_ToString"); + + parcMemory_Deallocate((void **) &string); + parcString_Release(&instance); +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_String); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} + + diff --git a/libparc/parc/algol/test/test_parc_Time.c b/libparc/parc/algol/test/test_parc_Time.c new file mode 100644 index 00000000..2befdde1 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_Time.c @@ -0,0 +1,144 @@ +/* + * 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 <LongBow/unit-test.h> +#include <LongBow/debugging.h> + +#include <parc/algol/parc_SafeMemory.h> +#include <parc/algol/parc_Memory.h> + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../parc_Time.c" + +LONGBOW_TEST_RUNNER(test_parc_Time) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(test_parc_Time) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(test_parc_Time) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcTime_TimevalAsString); + LONGBOW_RUN_TEST_CASE(Global, parcTime_TimevalAsISO8601); + LONGBOW_RUN_TEST_CASE(Global, parcTime_TimevalAsRFC3339); + LONGBOW_RUN_TEST_CASE(Global, parcTime_RFC3339_Now); + LONGBOW_RUN_TEST_CASE(Global, parcTime_NowTimeval); + LONGBOW_RUN_TEST_CASE(Global, parcTime_NowMicroseconds); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcTime_TimevalAsString) +{ + struct timeval timeval; + timeval.tv_sec = 0; + timeval.tv_usec = 1000; + + char *expected = "0.001000"; + char *actual = parcTime_TimevalAsString(timeval); + assertTrue(strcmp(expected, actual) == 0, "Expected %s, actual %s", expected, actual); + parcMemory_Deallocate((void **) &actual); +} + +LONGBOW_TEST_CASE(Global, parcTime_TimevalAsISO8601) +{ + struct timeval timeval; + timeval.tv_sec = 0; + timeval.tv_usec = 1000; + + char *expected = "1970-01-01 00:00:00.001000Z"; + + char actual[64]; + parcTime_TimevalAsISO8601(&timeval, actual); + + assertTrue(strcmp(expected, actual) == 0, "Expected %s, actual %s", expected, actual); +} + +LONGBOW_TEST_CASE(Global, parcTime_TimevalAsRFC3339) +{ + struct timeval timeval; + timeval.tv_sec = 0; + timeval.tv_usec = 1000; + + char *expected = "1970-01-01T00:00:00.001000Z"; + + char actual[64]; + parcTime_TimevalAsRFC3339(&timeval, actual); + + assertTrue(strcmp(expected, actual) == 0, "Expected %s, actual %s", expected, actual); +} + +LONGBOW_TEST_CASE(Global, parcTime_RFC3339_Now) +{ + struct timeval timeval; + gettimeofday(&timeval, NULL); + + char actual[64]; + parcTime_TimevalAsRFC3339(&timeval, actual); + printf("%s\n", actual); +} + +LONGBOW_TEST_CASE(Global, parcTime_NowTimeval) +{ + struct timeval result = parcTime_NowTimeval(); + assertTrue(result.tv_sec != 0, "Expected NOW to not be zero."); +} + +LONGBOW_TEST_CASE(Global, parcTime_NowMicroseconds) +{ + uint64_t result = parcTime_NowMicroseconds(); + assertTrue(result != 0, "Expected NOW to not be zero."); +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(test_parc_Time); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_TreeMap.c b/libparc/parc/algol/test/test_parc_TreeMap.c new file mode 100755 index 00000000..9355380b --- /dev/null +++ b/libparc/parc/algol/test/test_parc_TreeMap.c @@ -0,0 +1,1565 @@ +/* + * 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 <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <time.h> + +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +#include "../parc_TreeMap.c" + + +typedef struct { + int value; +} _Int; + +static _Int * +_int_Copy(const _Int *source); + +static bool +_int_Equals(const _Int *a, const _Int *b) +{ + return a->value == b->value; +} + +static int +_int_Compare(const _Int *a, const _Int *b) +{ + int result = 0; + if (a->value > b->value) { + result = 1; + } else if (a->value < b->value) { + result = -1; + } + + return result; +} + +parcObject_ExtendPARCObject(_Int, NULL, _int_Copy, NULL, _int_Equals, _int_Compare, NULL, NULL); + +static +parcObject_ImplementRelease(_int, _Int); + +static _Int * +_int_Create(const int value) +{ + _Int *newObj = parcObject_CreateInstance(_Int); + assertNotNull(newObj, "parcMemory_Allocate(%zu) returned NULL", sizeof(int)); + newObj->value = value; + return newObj; +} + +static _Int * +_int_Copy(const _Int *source) +{ + return _int_Create(source->value); +} + +static _Int * +_int_Set(_Int *obj, const int value) +{ + obj->value = value; + return obj; +} +static PARCBuffer* +strBuf(char *key) +{ + return parcBuffer_WrapCString(key); +} + + +LONGBOW_TEST_RUNNER(PARC_TreeMap) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); + LONGBOW_RUN_TEST_FIXTURE(Stress); +} + +LONGBOW_TEST_RUNNER_SETUP(PARC_TreeMap) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + + int seed = (int) time(NULL); + srandom(seed); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_RUNNER_TEARDOWN(PARC_TreeMap) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestRunner_GetName(testRunner), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Create); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Acquire); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Remove_Ordered); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Put_Release); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Put_Ordered); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Put_OutOfOrder); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Put_Overwrite); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_ReleaseTillEmpty); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Size_Empty); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Size); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Size_Overwrite); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Get_EmptyTree); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Get_NonExistent); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Get_First); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Get); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Get_Last); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Get_Biggest); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Get_Smallest); + + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_FirstKey); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_FirstEntry); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_LastKey); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_LastEntry); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_FirstKey_Empty); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_LastKey_Empty); + + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_LowerEntry); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_LowerKey); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_HigherEntry); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_HigherKey); + + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Remove_First); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Remove); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Remove_Last); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Remove_NonExistent); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_RemoveAndRelease_First); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_RemoveAndRelease); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_RemoveAndRelease_Last); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_RemoveAndRelease_NonExistent); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Remove_WithSuccessorNonRoot); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Remove_LeftChildRightChild); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Keys); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Values); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Equals_Empty); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Equals); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Equals_DifferentLength); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Equals_Not_Values); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Equals_Not_Keys); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Copy); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Copy_Direct); + + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Iterator); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_ValueIterator); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_KeyIterator); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Remove_Using_Iterator); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeMap_Remove_Element_Using_Iterator); +} + +#define N_TEST_ELEMENTS 42 + +typedef struct { + PARCTreeMap *testMap1; + PARCTreeMap *testMap2; + _Int *k[N_TEST_ELEMENTS]; + _Int *v[N_TEST_ELEMENTS]; +} TestData; + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + data->testMap1 = parcTreeMap_Create(); + data->testMap2 = parcTreeMap_CreateCustom((PARCTreeMap_CustomCompare *) _int_Compare); + + for (int i = 0; i < N_TEST_ELEMENTS; ++i) { + data->k[i] = _int_Create(i); + data->v[i] = _int_Create(i + 1000); + } + longBowTestCase_SetClipBoardData(testCase, data); + + return LONGBOW_STATUS_SUCCEEDED; +} + + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + parcTreeMap_Release(&data->testMap1); + parcTreeMap_Release(&data->testMap2); + + for (int i = 0; i < N_TEST_ELEMENTS; ++i) { + _int_Release(&(data->k[i])); + _int_Release(&(data->v[i])); + } + + parcMemory_Deallocate((void **) &data); + + /* + * uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + * if (outstandingAllocations != 0) { + * printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + * return LONGBOW_STATUS_MEMORYLEAK; + * } + */ + return LONGBOW_STATUS_SUCCEEDED; +} + +static +int +recursiveCheckBlackDepth(const PARCTreeMap *tree, const _RBNode *node) +{ + assertNotNull(tree, "Null tree?\n"); + assertNotNull(node, "Null node?\n"); + if (node == tree->nil) { + return 0; + } + int right_depth = recursiveCheckBlackDepth(tree, node->rightChild); + int left_depth = recursiveCheckBlackDepth(tree, node->leftChild); + assertTrue(right_depth == left_depth, "Wrong depth!!\n"); + if (_rbNodeColor(node) == BLACK) { + return right_depth + 1; + } + return right_depth; +} + +static +void +rbCheckTree(const PARCTreeMap *tree) +{ + assertNotNull(tree, "Tree can't be NULL"); + _rbNodeAssertTreeInvariants(tree); + if (tree->size > 0) { + recursiveCheckBlackDepth(tree, tree->root); + } +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Remove_Ordered) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCTreeMap *tree1 = data->testMap1; + + for (int i = 0; i < 16; i++) { + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + } + + for (int i = 0; i < 14; i++) { + //rbPrintTreeString(tree1); + PARCObject *value = parcTreeMap_Remove(tree1, data->k[i]); + assertNotNull(value, "Data is null!"); + assertTrue(_int_Equals(value, data->v[i]), "Expect the ordered value."); + parcObject_Release(&value); + } +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Create) +{ + PARCTreeMap *map = parcTreeMap_Create(); + parcTreeMap_Release(&map); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Acquire) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCTreeMap *map = parcTreeMap_Acquire(data->testMap1); + parcTreeMap_Release(&map); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Put_Release) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCTreeMap *tree = data->testMap1; + + PARCBuffer *value1 = strBuf("value 1"); + PARCBuffer *key1 = strBuf("1"); + PARCBuffer *value2 = strBuf("value 2"); + PARCBuffer *key2 = strBuf("2"); + PARCBuffer *value3 = strBuf("value 3"); + PARCBuffer *key3 = strBuf("3"); + + parcTreeMap_Put(tree, key1, value1); + parcTreeMap_Put(tree, key2, value2); + parcTreeMap_Put(tree, key3, value3); + + parcBuffer_Release(&key1); + parcBuffer_Release(&value1); + parcBuffer_Release(&key2); + parcBuffer_Release(&value2); + parcBuffer_Release(&key3); + parcBuffer_Release(&value3); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Put_Overwrite) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCTreeMap *tree = data->testMap1; + + parcTreeMap_Put(tree, data->k[1], data->v[1]); + parcTreeMap_Put(tree, data->k[2], data->v[2]); + parcTreeMap_Put(tree, data->k[3], data->v[3]); + parcTreeMap_Put(tree, data->k[3], data->v[4]); + parcTreeMap_Put(tree, data->k[3], data->v[5]); + + assertTrue(3 == parcTreeMap_Size(tree), "Wrong size of tree should stay at 3"); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Put_Ordered) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCTreeMap *tree = data->testMap1; + + parcTreeMap_Put(tree, data->k[1], data->v[1]); + parcTreeMap_Put(tree, data->k[2], data->v[2]); + parcTreeMap_Put(tree, data->k[3], data->v[3]); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Put_OutOfOrder) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCTreeMap *tree1 = data->testMap1; + + parcTreeMap_Put(tree1, data->k[4], data->v[4]); + parcTreeMap_Put(tree1, data->k[2], data->v[2]); + parcTreeMap_Put(tree1, data->k[3], data->v[3]); + parcTreeMap_Put(tree1, data->k[1], data->v[1]); + + PARCTreeMap *tree2 = data->testMap2; + parcTreeMap_Put(tree2, data->k[1], data->v[1]); + parcTreeMap_Put(tree2, data->k[3], data->v[3]); + parcTreeMap_Put(tree2, data->k[2], data->v[2]); + parcTreeMap_Put(tree2, data->k[4], data->v[4]); + + assertTrue(parcTreeMap_Equals(tree1, tree2), "Expect trees to be Equal"); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Size_Empty) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCTreeMap *tree = data->testMap1; + + assertTrue(0 == parcTreeMap_Size(tree), "Wrong size of tree - empty, start"); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Size) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCTreeMap *tree = data->testMap1; + + parcTreeMap_Put(tree, data->k[4], data->v[4]); + parcTreeMap_Put(tree, data->k[2], data->v[2]); + parcTreeMap_Put(tree, data->k[3], data->v[3]); + + assertTrue(3 == parcTreeMap_Size(tree), "Wrong size of tree after add 3"); + + parcTreeMap_Put(tree, data->k[1], data->v[1]); + + assertTrue(4 == parcTreeMap_Size(tree), "Wrong size of tree after add 1 more"); + + parcTreeMap_RemoveAndRelease(tree, data->k[2]); + + size_t size = parcTreeMap_Size(tree); + + assertTrue(3 == size, "Wrong size of tree after 1 delete (%zu instead of 3)", size); + + parcTreeMap_Put(tree, data->k[7], data->v[7]); + + assertTrue(4 == parcTreeMap_Size(tree), "Wrong size of tree after add 1 more"); + + parcTreeMap_RemoveAndRelease(tree, data->k[3]); + assertTrue(3 == parcTreeMap_Size(tree), "Wrong size of tree after del 1 more - 3"); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_ReleaseTillEmpty) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCTreeMap *tree1 = data->testMap1; + PARCTreeMap *tree2 = data->testMap2; + + // This order of puts are removes exercises code paths + // in TreeMap not exercised in any other place. + int idx1a[7] = { 4, 2, 3, 1, 5, 7, 6 }; + int idx1b[7] = { 3, 1, 4, 2, 6, 5, 7 }; + int idx2a[7] = { 4, 6, 5, 7, 3, 1, 2 }; + int idx2b[7] = { 5, 7, 4, 6, 2, 3, 1 }; + + for (int i = 0; i < 7; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[idx1a[i]], data->v[idx1a[i]]); + parcTreeMap_Put(tree2, data->k[idx2a[i]], data->v[idx2a[i]]); + } + + for (int i = 0; i < 7; ++i) { + parcTreeMap_RemoveAndRelease(tree1, data->k[idx1b[i]]); + parcTreeMap_RemoveAndRelease(tree2, data->k[idx2b[i]]); + } + + size_t size; + size = parcTreeMap_Size(tree1); + + assertTrue(0 == size, "Wrong size of tree - empty (got %zu)", size); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Size_Overwrite) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCTreeMap *tree = data->testMap1; + + parcTreeMap_Put(tree, data->k[4], data->v[4]); + parcTreeMap_Put(tree, data->k[2], data->v[2]); + parcTreeMap_Put(tree, data->k[3], data->v[3]); + + // Size is 3 here, we'll insert the same number now.. + + parcTreeMap_Put(tree, data->k[3], data->v[23]); + + assertTrue(3 == parcTreeMap_Size(tree), "Wrong size of tree after overwrite"); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Get_EmptyTree) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCTreeMap *tree = data->testMap1; + + PARCObject *value = parcTreeMap_Get(tree, data->k[1]); + + assertTrue(NULL == value, "Object did not exist, must return NULL"); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Get_NonExistent) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCTreeMap *tree = data->testMap1; + + for (long i = 1; i < 10; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree, data->k[i], data->v[i]); + } + + PARCObject *value = parcTreeMap_Get(tree, data->k[23]); + + assertTrue(NULL == value, "Object did not exist, must return NULL"); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Get_First) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCTreeMap *tree = data->testMap1; + + for (long i = 1; i < 4; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree, data->k[i], data->v[i]); + } + + PARCObject *value = parcTreeMap_Get(tree, data->k[1]); + + assertTrue(_int_Equals(data->v[1], value), "Wrong value, got %ld", (long) value); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Get) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCTreeMap *tree = data->testMap1; + + for (long i = 1; i < 10; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree, data->k[i], data->v[i]); + } + + PARCObject *value = parcTreeMap_Get(tree, data->k[4]); + + assertTrue(_int_Equals(data->v[4], value), "Wrong value, got %ld", (long) value); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Get_Last) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCTreeMap *tree = data->testMap1; + + for (long i = 1; i < 10; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree, data->k[i], data->v[i]); + } + + PARCObject *value = parcTreeMap_Get(tree, data->k[9]); + + assertTrue(_int_Equals(data->v[9], value), "Wrong value, got %ld", (long) value); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Get_Smallest) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCTreeMap *tree = data->testMap1; + + for (long i = 30; i < 40; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree, data->k[i], data->v[i]); + } + for (long i = 1; i < 10; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree, data->k[i], data->v[i]); + } + for (long i = 20; i < 30; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree, data->k[i], data->v[i]); + } + + + PARCObject *value = parcTreeMap_Get(tree, data->k[1]); + + assertTrue(_int_Equals(data->v[1], value), "Wrong value, got %ld", (long) value); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Get_Biggest) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCTreeMap *tree = data->testMap1; + + for (long i = 30; i < 40; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree, data->k[i], data->v[i]); + } + for (long i = 1; i < 10; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree, data->k[i], data->v[i]); + } + for (long i = 20; i < 30; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree, data->k[i], data->v[i]); + } + + PARCObject *value = parcTreeMap_Get(tree, data->k[39]); + + assertTrue(_int_Equals(data->v[39], value), "Wrong value, got %ld", (long) value); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_FirstEntry) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCTreeMap *tree = data->testMap1; + + for (long i = 30; i < 40; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree, data->k[i], data->v[i]); + } + for (long i = 1; i < 10; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree, data->k[i], data->v[i]); + } + for (long i = 20; i < 30; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree, data->k[i], data->v[i]); + } + + PARCKeyValue *entry = parcTreeMap_GetFirstEntry(tree); + + assertTrue(_int_Equals(data->k[1], parcKeyValue_GetKey(entry)), + "Wrong value, got %ld", (long) parcKeyValue_GetKey(entry)); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_FirstKey) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCTreeMap *tree = data->testMap1; + + for (long i = 30; i < 40; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree, data->k[i], data->v[i]); + } + for (long i = 1; i < 10; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree, data->k[i], data->v[i]); + } + for (long i = 20; i < 30; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree, data->k[i], data->v[i]); + } + + PARCObject *key = parcTreeMap_GetFirstKey(tree); + + assertTrue(_int_Equals(data->k[1], key), "Wrong value, got %ld", (long) key); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_FirstKey_Empty) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCTreeMap *tree = data->testMap1; + + PARCObject *key = parcTreeMap_GetFirstKey(tree); + + assertNull(key, "Should get NULL on empty tree"); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_LastKey_Empty) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCTreeMap *tree = data->testMap1; + + PARCObject *key = parcTreeMap_GetLastKey(tree); + + assertNull(key, "Should get NULL on empty tree"); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_LastEntry) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCTreeMap *tree = data->testMap1; + + for (long i = 30; i < 40; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree, data->k[i], data->v[i]); + } + for (long i = 1; i < 10; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree, data->k[i], data->v[i]); + } + for (long i = 20; i < 30; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree, data->k[i], data->v[i]); + } + + PARCKeyValue *entry = parcTreeMap_GetLastEntry(tree); + + assertTrue(_int_Equals(data->k[39], parcKeyValue_GetKey(entry)), + "Wrong value, got %ld", (long) parcKeyValue_GetKey(entry)); +} + + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_LastKey) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCTreeMap *tree = data->testMap1; + + for (long i = 30; i < 40; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree, data->k[i], data->v[i]); + } + for (long i = 1; i < 10; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree, data->k[i], data->v[i]); + } + for (long i = 20; i < 30; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree, data->k[i], data->v[i]); + } + + PARCObject *key = parcTreeMap_GetLastKey(tree); + + assertTrue(_int_Equals(data->k[39], key), "Wrong value, got %ld", (long) key); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Remove_First) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCTreeMap *tree1 = data->testMap1; + PARCTreeMap *tree2 = data->testMap2; + + for (int i = 30; i < 40; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + parcTreeMap_Put(tree2, data->k[i], data->v[i]); + } + + parcTreeMap_Put(tree1, data->k[1], data->v[1]); + + for (int i = 2; i < 10; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + parcTreeMap_Put(tree2, data->k[i], data->v[i]); + } + + for (int i = 20; i < 30; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + parcTreeMap_Put(tree2, data->k[i], data->v[i]); + } + + PARCObject *value = parcTreeMap_Remove(tree1, data->k[1]); + parcObject_Release(&value); + + assertTrue(parcTreeMap_Equals(tree1, tree2), "Trees dont match after remove"); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Remove) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCTreeMap *tree1 = data->testMap1; + PARCTreeMap *tree2 = data->testMap2; + + for (int i = 31; i < 40; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + parcTreeMap_Put(tree2, data->k[i], data->v[i]); + } + + parcTreeMap_Put(tree1, data->k[30], data->v[30]); + + for (int i = 2; i < 10; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + parcTreeMap_Put(tree2, data->k[i], data->v[i]); + } + + for (int i = 20; i < 30; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + parcTreeMap_Put(tree2, data->k[i], data->v[i]); + } + + PARCObject *value = parcTreeMap_Remove(tree1, data->k[30]); + parcObject_Release(&value); + + assertTrue(parcTreeMap_Equals(tree1, tree2), "Trees dont match after remove"); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Remove_Last) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCTreeMap *tree1 = data->testMap1; + PARCTreeMap *tree2 = data->testMap2; + + for (int i = 30; i < 40; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + parcTreeMap_Put(tree2, data->k[i], data->v[i]); + } + + parcTreeMap_Put(tree1, data->k[41], data->v[41]); + + for (int i = 2; i < 10; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + parcTreeMap_Put(tree2, data->k[i], data->v[i]); + } + for (int i = 20; i < 30; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + parcTreeMap_Put(tree2, data->k[i], data->v[i]); + } + + PARCObject *value = parcTreeMap_Remove(tree1, data->k[41]); + assertNotNull(value, "Expected to find some object."); + assertTrue(_int_Equals(data->v[41], value), "Expected value 41 in return"); + parcObject_Release(&value); + + assertTrue(parcTreeMap_Equals(tree1, tree2), "Trees don't match after remove"); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_RemoveAndRelease_First) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCTreeMap *tree1 = data->testMap1; + PARCTreeMap *tree2 = data->testMap2; + + for (int i = 30; i < 40; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + parcTreeMap_Put(tree2, data->k[i], data->v[i]); + } + + parcTreeMap_Put(tree1, data->k[1], data->v[1]); + + for (int i = 2; i < 10; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + parcTreeMap_Put(tree2, data->k[i], data->v[i]); + } + for (int i = 20; i < 30; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + parcTreeMap_Put(tree2, data->k[i], data->v[i]); + } + + parcTreeMap_RemoveAndRelease(tree1, data->k[1]); + + assertTrue(parcTreeMap_Equals(tree1, tree2), "Trees dont match after remove"); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_RemoveAndRelease) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCTreeMap *tree1 = data->testMap1; + PARCTreeMap *tree2 = data->testMap2; + + for (int i = 31; i < 40; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + parcTreeMap_Put(tree2, data->k[i], data->v[i]); + } + + parcTreeMap_Put(tree1, data->k[30], data->v[30]); + + for (int i = 2; i < 10; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + parcTreeMap_Put(tree2, data->k[i], data->v[i]); + } + for (int i = 20; i < 30; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + parcTreeMap_Put(tree2, data->k[i], data->v[i]); + } + + parcTreeMap_RemoveAndRelease(tree1, data->k[30]); + assertTrue(parcTreeMap_Equals(tree1, tree2), "Trees dont match after remove"); + + for (int i = 20; i < 30; i++) { + parcTreeMap_RemoveAndRelease(tree1, data->k[i]); + parcTreeMap_RemoveAndRelease(tree2, data->k[49 - i]); + } + assertTrue(parcTreeMap_Equals(tree1, tree2), "Trees dont match after remove"); + + for (int i = 2; i < 10; i++) { + parcTreeMap_RemoveAndRelease(tree1, data->k[i]); + parcTreeMap_RemoveAndRelease(tree2, data->k[11 - i]); + } + assertTrue(parcTreeMap_Equals(tree1, tree2), "Trees dont match after remove"); + + for (int i = 31; i < 40; i++) { + parcTreeMap_RemoveAndRelease(tree1, data->k[i]); + parcTreeMap_RemoveAndRelease(tree2, data->k[70 - i]); + } + assertTrue(parcTreeMap_Equals(tree1, tree2), "Trees dont match after remove"); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Remove_NonExistent) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCTreeMap *tree1 = data->testMap1; + PARCTreeMap *tree2 = data->testMap2; + + for (int i = 30; i < 40; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + parcTreeMap_Put(tree2, data->k[i], data->v[i]); + } + for (int i = 2; i < 10; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + parcTreeMap_Put(tree2, data->k[i], data->v[i]); + } + for (int i = 20; i < 30; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + parcTreeMap_Put(tree2, data->k[i], data->v[i]); + } + + PARCObject *element = parcTreeMap_Remove(tree1, data->k[0]); + + assertNull(element, "Return value must be NULL on non existing element"); + assertTrue(parcTreeMap_Equals(tree1, tree2), "Trees dont match after remove"); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_RemoveAndRelease_NonExistent) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCTreeMap *tree1 = data->testMap1; + PARCTreeMap *tree2 = data->testMap2; + + for (int i = 30; i < 40; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + parcTreeMap_Put(tree2, data->k[i], data->v[i]); + } + for (int i = 2; i < 10; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + parcTreeMap_Put(tree2, data->k[i], data->v[i]); + } + for (int i = 20; i < 30; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + parcTreeMap_Put(tree2, data->k[i], data->v[i]); + } + + parcTreeMap_RemoveAndRelease(tree1, data->k[0]); + + assertTrue(parcTreeMap_Equals(tree1, tree2), "Trees dont match after remove"); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Remove_WithSuccessorNonRoot) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCTreeMap *tree1 = data->testMap1; + PARCTreeMap *tree2 = data->testMap2; + + int idx1[15] = { 8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15 }; + int idx2[13] = { 8, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15 }; + + for (int i = 0; i < 15; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[idx1[i]], data->v[idx1[i]]); + } + + for (int i = 0; i < 13; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree2, data->k[idx2[i]], data->v[idx2[i]]); + } + + _Int *key = _int_Create(4); + parcTreeMap_RemoveAndRelease(tree1, key); + parcTreeMap_RemoveAndRelease(tree1, _int_Set(key, 12)); + _int_Release(&key); + + assertTrue(parcTreeMap_Equals(tree1, tree2), "Trees dont match after remove"); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Remove_LeftChildRightChild) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCTreeMap *tree1 = data->testMap1; + PARCTreeMap *tree2 = data->testMap2; + + int idx1[15] = { 8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15 }; + + for (int i = 0; i < 15; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[idx1[i]], data->v[idx1[i]]); + parcTreeMap_Put(tree2, data->k[idx1[i]], data->v[idx1[i]]); + } + + _Int *key = _int_Create(0); + parcTreeMap_RemoveAndRelease(tree1, _int_Set(key, 13)); + parcTreeMap_RemoveAndRelease(tree1, _int_Set(key, 7)); + parcTreeMap_RemoveAndRelease(tree1, _int_Set(key, 14)); + parcTreeMap_RemoveAndRelease(tree1, _int_Set(key, 6)); + parcTreeMap_RemoveAndRelease(tree1, _int_Set(key, 15)); + parcTreeMap_RemoveAndRelease(tree1, _int_Set(key, 12)); + parcTreeMap_RemoveAndRelease(tree1, _int_Set(key, 11)); + parcTreeMap_RemoveAndRelease(tree1, _int_Set(key, 10)); + parcTreeMap_RemoveAndRelease(tree1, _int_Set(key, 9)); + parcTreeMap_RemoveAndRelease(tree1, _int_Set(key, 8)); + parcTreeMap_RemoveAndRelease(tree1, _int_Set(key, 5)); + parcTreeMap_RemoveAndRelease(tree1, _int_Set(key, 4)); + parcTreeMap_RemoveAndRelease(tree1, _int_Set(key, 3)); + parcTreeMap_RemoveAndRelease(tree1, _int_Set(key, 2)); + parcTreeMap_RemoveAndRelease(tree1, _int_Set(key, 1)); + + parcTreeMap_RemoveAndRelease(tree2, _int_Set(key, 1)); + parcTreeMap_RemoveAndRelease(tree2, _int_Set(key, 2)); + parcTreeMap_RemoveAndRelease(tree2, _int_Set(key, 3)); + parcTreeMap_RemoveAndRelease(tree2, _int_Set(key, 4)); + parcTreeMap_RemoveAndRelease(tree2, _int_Set(key, 5)); + parcTreeMap_RemoveAndRelease(tree2, _int_Set(key, 6)); + parcTreeMap_RemoveAndRelease(tree2, _int_Set(key, 7)); + parcTreeMap_RemoveAndRelease(tree2, _int_Set(key, 8)); + parcTreeMap_RemoveAndRelease(tree2, _int_Set(key, 9)); + parcTreeMap_RemoveAndRelease(tree2, _int_Set(key, 10)); + parcTreeMap_RemoveAndRelease(tree2, _int_Set(key, 11)); + parcTreeMap_RemoveAndRelease(tree2, _int_Set(key, 12)); + parcTreeMap_RemoveAndRelease(tree2, _int_Set(key, 13)); + parcTreeMap_RemoveAndRelease(tree2, _int_Set(key, 14)); + parcTreeMap_RemoveAndRelease(tree2, _int_Set(key, 15)); + _int_Release(&key); + + assertTrue(parcTreeMap_Equals(tree1, tree2), "Trees dont match after remove"); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_RemoveAndRelease_Last) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCTreeMap *tree1 = data->testMap1; + PARCTreeMap *tree2 = data->testMap2; + + for (int i = 30; i < 40; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + parcTreeMap_Put(tree2, data->k[i], data->v[i]); + } + parcTreeMap_Put(tree1, data->k[41], data->v[41]); + for (int i = 2; i < 10; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + parcTreeMap_Put(tree2, data->k[i], data->v[i]); + } + for (int i = 20; i < 30; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + parcTreeMap_Put(tree2, data->k[i], data->v[i]); + } + + parcTreeMap_RemoveAndRelease(tree1, data->k[41]); + + assertTrue(parcTreeMap_Equals(tree1, tree2), "Trees dont match after remove"); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_LowerEntry) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCTreeMap *tree1 = data->testMap1; + + // Empty Tree + PARCKeyValue *kv = parcTreeMap_GetLowerEntry(tree1, data->k[23]); + assertNull(kv, "Expected a NULL return for LowerEntry() on empty tree"); + + // Fill Tree + int max = N_TEST_ELEMENTS - 1; + for (int i = 21; i <= max; ++i) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + } + for (int i = 1; i < 21; ++i) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + } + + // Using lowest key in tree + kv = parcTreeMap_GetLowerEntry(tree1, data->k[1]); + assertNull(kv, "Expected a NULL return for no lower entry"); + + // On all entries except the lowest tree + for (int i = max; i > 1; --i) { + kv = parcTreeMap_GetLowerEntry(tree1, data->k[i]); + assertNotNull(kv, "Expected a lower entry to exist"); + _Int *key = (_Int *) parcKeyValue_GetKey(kv); + assertTrue(_int_Equals(key, data->k[i - 1]), + "Expected entry with key %d, got %d", + data->k[i - 1]->value, key->value); + } +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_LowerKey) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCTreeMap *tree1 = data->testMap1; + + // Empty Tree + _Int *key = (_Int *) parcTreeMap_GetLowerKey(tree1, data->k[23]); + assertNull(key, "Expected a NULL return for LowerEntry() on empty tree"); + + int max = N_TEST_ELEMENTS - 1; + for (int i = 21; i <= max; ++i) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + } + for (int i = 1; i < 21; ++i) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + } + + // Using lowest key in tree + key = (_Int *) parcTreeMap_GetLowerKey(tree1, data->k[1]); + assertNull(key, "Expected a NULL return for no lower entry"); + + // On all entries except the lowest tree + for (int i = max; i > 1; --i) { + key = parcTreeMap_GetLowerKey(tree1, data->k[i]); + assertNotNull(key, "Expected a lower entry to exist"); + assertTrue(_int_Equals(key, data->k[i - 1]), + "Expected entry with key %d, got %d", + data->k[i - 1]->value, key->value); + } +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_HigherEntry) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCTreeMap *tree1 = data->testMap1; + + // Empty Tree + PARCKeyValue *kv = parcTreeMap_GetHigherEntry(tree1, data->k[23]); + assertNull(kv, "Expected a NULL return for HigherEntry() on empty tree"); + + int max = N_TEST_ELEMENTS - 2; + for (int i = 21; i <= max; ++i) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + } + for (int i = 1; i < 21; ++i) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + } + + // Using highest key in tree + kv = parcTreeMap_GetHigherEntry(tree1, data->k[max]); + assertNull(kv, "Expected a NULL return for no higher entry"); + + // On all entries except the lowest tree + for (int i = 1; i < max; ++i) { + kv = parcTreeMap_GetHigherEntry(tree1, data->k[i]); + assertNotNull(kv, "Expected a higher entry to exist"); + _Int *key = (_Int *) parcKeyValue_GetKey(kv); + assertTrue(_int_Equals(key, data->k[i + 1]), + "Expected entry with key %d, got %d", + data->k[i + 1]->value, key->value); + } +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_HigherKey) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCTreeMap *tree1 = data->testMap1; + + // Empty Tree + _Int *key = (_Int *) parcTreeMap_GetHigherKey(tree1, data->k[23]); + assertNull(key, "Expected a NULL return for LowerEntry() on empty tree"); + + int max = N_TEST_ELEMENTS - 2; + for (int i = 21; i <= max; ++i) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + } + for (int i = 1; i < 21; ++i) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + } + + key = (_Int *) parcTreeMap_GetHigherEntry(tree1, data->k[max]); + assertNull(key, "Expected a NULL return for no higher entry"); + + for (int i = 1; i < max; ++i) { + key = (_Int *) parcTreeMap_GetHigherKey(tree1, data->k[i]); + assertNotNull(key, "Expected a higher entry to exist"); + assertTrue(_int_Equals(key, data->k[i + 1]), + "Expected entry with key %d, got %d", + data->k[i + 1]->value, key->value); + } +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Keys) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCTreeMap *tree1 = data->testMap1; + + PARCList *list = parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList); + + // Insert in tree out of order + for (int i = 10; i < 20; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + } + for (int i = 1; i < 10; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + } + + // Insert in list in order + for (int i = 1; i < 20; i++) { + // Add some elements to the tree + parcList_Add(list, data->k[i]); + } + + PARCList *keys = parcTreeMap_AcquireKeys(tree1); + + assertTrue(parcList_Equals(list, keys), "Key list doesnt' match"); + + parcList_Release(&keys); + parcList_Release(&list); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Values) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCTreeMap *tree1 = data->testMap1; + + PARCList *list = parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList); + + // Insert in tree out of order + for (int i = 10; i < 20; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + } + for (int i = 1; i < 10; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + } + + // Insert in list in order + for (int i = 1; i < 20; i++) { + // Add some elements to the tree + parcList_Add(list, data->v[i]); + } + + PARCList *values = parcTreeMap_AcquireValues(tree1); + + assertTrue(parcList_Equals(list, values), "Key list doesnt' match"); + + parcList_Release(&values); + parcList_Release(&list); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Equals_Empty) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCTreeMap *tree1 = data->testMap1; + PARCTreeMap *tree2 = data->testMap2; + + assertTrue(parcTreeMap_Equals(tree1, tree2), "Empty lists are not equal"); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Equals_DifferentLength) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCTreeMap *tree1 = data->testMap1; + PARCTreeMap *tree2 = data->testMap2; + + + for (int i = 1; i < 20; i++) { + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + parcTreeMap_Put(tree2, data->k[i], data->v[i]); + } + + parcTreeMap_Put(tree2, data->k[41], data->v[41]); + + assertFalse(parcTreeMap_Equals(tree1, tree2), "Lists are equal"); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Equals_Not_Values) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCTreeMap *tree1 = data->testMap1; + PARCTreeMap *tree2 = data->testMap2; + + for (int i = 1; i < 20; i++) { + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + parcTreeMap_Put(tree2, data->k[i], data->v[20 - i]); + } + + assertFalse(parcTreeMap_Equals(tree1, tree2), "Lists are equal"); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Equals_Not_Keys) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCTreeMap *tree1 = data->testMap1; + PARCTreeMap *tree2 = data->testMap2; + + for (int i = 1; i < 20; i++) { + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + parcTreeMap_Put(tree2, data->k[i + 1], data->v[i]); + } + assertTrue(parcTreeMap_Size(tree1) == parcTreeMap_Size(tree2), "Expect trees to have the same size."); + + assertFalse(parcTreeMap_Equals(tree1, tree2), "Lists should not be equal"); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Equals) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCTreeMap *tree1 = data->testMap1; + PARCTreeMap *tree2 = data->testMap2; + + for (int i = 1; i < 40; i++) { + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + parcTreeMap_Put(tree2, data->k[40 - i], data->v[40 - i]); + } + + assertTrue(parcTreeMap_Equals(tree1, tree2), "Lists are not equal"); +} + + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Copy) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCTreeMap *tree1 = data->testMap1; + PARCTreeMap *treeCopy = parcTreeMap_Copy(tree1); + + for (int i = 1; i < 10; i++) { + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + } + + assertFalse(parcTreeMap_Equals(tree1, treeCopy), "Lists are not equal"); + + parcTreeMap_Release(&treeCopy); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Copy_Direct) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCTreeMap *tree1 = data->testMap1; + + for (int i = 1; i < 20; i++) { + parcTreeMap_Put(tree1, data->k[i], data->v[i]); + } + + PARCTreeMap *treeCopy = parcTreeMap_Copy(tree1); + + assertTrue(parcTreeMap_Equals(tree1, treeCopy), "Lists are not equal"); + + parcTreeMap_Release(&treeCopy); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_ValueIterator) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCTreeMap *tree1 = data->testMap1; + + int idx1[15] = { 8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15 }; + + for (int i = 0; i < 15; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[idx1[i]], data->v[idx1[i]]); + } + + PARCIterator *it = parcTreeMap_CreateValueIterator(tree1); + + for (int idx = 1; parcIterator_HasNext(it); ++idx) { + _Int *value = (_Int *) parcIterator_Next(it); + assertTrue(_int_Equals(value, data->v[idx]), "Expected value %d got %d", data->v[idx]->value, value->value); + } + + parcIterator_Release(&it); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_KeyIterator) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCTreeMap *tree1 = data->testMap1; + + int idx1[15] = { 8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15 }; + + for (int i = 0; i < 15; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[idx1[i]], data->v[idx1[i]]); + } + + PARCIterator *it = parcTreeMap_CreateKeyIterator(tree1); + + for (int idx = 1; parcIterator_HasNext(it); ++idx) { + _Int *key = (_Int *) parcIterator_Next(it); + assertTrue(_int_Equals(key, data->k[idx]), + "Expected value %d got %d", + data->k[idx]->value, + key->value); + } + + parcIterator_Release(&it); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Iterator) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCTreeMap *tree1 = data->testMap1; + + int idx1[15] = { 8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15 }; + + for (int i = 0; i < 15; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[idx1[i]], data->v[idx1[i]]); + } + + PARCIterator *it = parcTreeMap_CreateKeyValueIterator(tree1); + + for (int idx = 1; parcIterator_HasNext(it); ++idx) { + PARCKeyValue *kv = (PARCKeyValue *) parcIterator_Next(it); + assertTrue(_int_Equals((_Int *) parcKeyValue_GetKey(kv), data->k[idx]), + "Expected value %d got %d", + data->k[idx]->value, + ((_Int *) parcKeyValue_GetKey(kv))->value); + } + + parcIterator_Release(&it); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Remove_Using_Iterator) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCTreeMap *tree1 = data->testMap1; + + int idx1[15] = { 8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15 }; + + for (int i = 0; i < 15; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[idx1[i]], data->v[idx1[i]]); + } + + PARCIterator *it = parcTreeMap_CreateKeyValueIterator(tree1); + for (int idx = 1; parcIterator_HasNext(it); ++idx) { + parcIterator_Next(it); + parcIterator_Remove(it); + } + parcIterator_Release(&it); + + assertTrue(parcTreeMap_Size(tree1) == 0, "Expect the tree to be empty after removes."); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeMap_Remove_Element_Using_Iterator) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCTreeMap *tree1 = data->testMap1; + PARCTreeMap *tree2 = data->testMap1; + + int idx1[15] = { 8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 15, 13 }; //Missing 11 + + for (int i = 0; i < 14; i++) { + // Add some elements to the tree + parcTreeMap_Put(tree1, data->k[idx1[i]], data->v[idx1[i]]); + parcTreeMap_Put(tree2, data->k[idx1[i]], data->v[idx1[i]]); + } + + parcTreeMap_Put(tree1, data->k[11], data->v[11]); + + + PARCIterator *it = parcTreeMap_CreateKeyValueIterator(tree1); + for (int idx = 1; parcIterator_HasNext(it); ++idx) { + parcIterator_Next(it); + if (idx == 11) { + parcIterator_Remove(it); + } + } + parcIterator_Release(&it); + + assertTrue(parcTreeMap_Equals(tree1, tree2), "Expect the trees to be equal after remove."); +} + +LONGBOW_TEST_FIXTURE(Local) +{ + //LONGBOW_RUN_TEST_CASE(Local, PARC_TreeMap_EnsureRemaining_NonEmpty); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Stress) +{ + // LongBow could use a command line option to enable/disable tests + // See LongBow issue #5 + if (getenv("LongBowStress")) { + LONGBOW_RUN_TEST_CASE(Stress, PARC_TreeMap_ExerciseRandomSeededSmall); + LONGBOW_RUN_TEST_CASE(Stress, PARC_TreeMap_ExerciseRandomSeeded); + } +} + +LONGBOW_TEST_FIXTURE_SETUP(Stress) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Stress) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Stress, PARC_TreeMap_ExerciseRandomSeededSmall) +{ + unsigned seed; + char *seedString; + + seedString = getenv("RBSeed"); + if (seedString) { + seed = (unsigned) atol(seedString); + } else { + seed = 4179329122; // known to fail + } + + for (int j = 0; j < 1; j++) { + // this test case should obtain a seed and number of iterations from a + // command line option once LongBow has that feature available + + srandom(seed); + PARCTreeMap *tree = parcTreeMap_Create(); + + int inserts = 0; + int deletes = 0; + + for (int i = 0; i < 100; i++) { + intptr_t item = 1 + (random() % 100); + int operation = random() % 1000; + if (operation < 400) { + inserts++; + parcTreeMap_Put(tree, (void *) item, (void *) (item << 8)); + } else { + deletes++; + parcTreeMap_Remove(tree, (void *) item); + } + rbCheckTree(tree); + } + + parcTreeMap_Release(&tree); + } +} + +LONGBOW_TEST_CASE(Stress, PARC_TreeMap_ExerciseRandomSeeded) +{ + PARCTreeMap *tree1; + unsigned seed; + char *seedString; + + // this test case should obtain a seed and number of iterations from a + // command line option once LongBow has that feature available + seedString = getenv("RBSeed"); + if (seedString) { + seed = (unsigned) atol(seedString); + } else { + seed = 4179329122; // known to fail + } + + srandom(seed); + + tree1 = parcTreeMap_Create(); + + int inserts = 0; + int deletes = 0; + + for (int i = 0; i < 100000; i++) { + intptr_t item = 1 + (random() % 10000); + int operation = random() % 1000; + if (operation < 400) { + inserts++; + parcTreeMap_Put(tree1, (void *) item, (void *) (item << 8)); + } else { + deletes++; + parcTreeMap_Remove(tree1, (void *) item); + } + rbCheckTree(tree1); + } + + parcTreeMap_Release(&tree1); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(PARC_TreeMap); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_TreeRedBlack.c b/libparc/parc/algol/test/test_parc_TreeRedBlack.c new file mode 100755 index 00000000..cf2f19ca --- /dev/null +++ b/libparc/parc/algol/test/test_parc_TreeRedBlack.c @@ -0,0 +1,1462 @@ +/* + * 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 <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <time.h> + +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +#include "../parc_TreeRedBlack.c" + + +void * +keyNewInt(int key) +{ + int *newKey = parcMemory_Allocate(sizeof(int)); + assertNotNull(newKey, "parcMemory_Allocate(%zu) returned NULL", sizeof(int)); + *newKey = key; + return newKey; +} + +void * +valueNewInt(int value) +{ + int *newValue = parcMemory_Allocate(sizeof(int)); + assertNotNull(newValue, "parcMemory_Allocate(%zu) returned NULL", sizeof(int)); + *newValue = value; + return newValue; +} + +void * +keyCopy(const void *key) +{ + return keyNewInt(*(int *) key); +} + +void * +valueCopy(const void *value) +{ + return valueNewInt(*(int *) value); +} + +void * +keyNew(char *key) +{ + return parcMemory_StringDuplicate(key, strlen(key)); +} + +void * +valueNew(char *value) +{ + return parcMemory_StringDuplicate(value, strlen(value)); +} + +int +intComp(const void *key1, const void *key2) +{ + if (*(int *) key1 < *(int *) key2) { + return -1; + } + if (*(int *) key1 == *(int *) key2) { + return 0; + } + return 1; +} + +bool +intEquals(const void *int1, const void *int2) +{ + return intComp(int1, int2) == 0; +} + +int +pointerComp(const void *key1, const void *key2) +{ + if (key1 < key2) { + return -1; + } + if (key1 == key2) { + return 0; + } + return 1; +} + +int +stringComp(const void *key1, const void *key2) +{ + // We assume all keys are strings. + return strcmp(key1, key2); +} + +void +keyFree(void **value) +{ + parcMemory_Deallocate((void **) value); + *value = NULL; +} + +void +valueFree(void **key) +{ + parcMemory_Deallocate((void **) key); + *key = NULL; +} + + +LONGBOW_TEST_RUNNER(PARC_TreeRedBlack) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); + LONGBOW_RUN_TEST_FIXTURE(Stress); +} + +LONGBOW_TEST_RUNNER_SETUP(PARC_TreeRedBlack) +{ + int seed = (int) time(NULL); + printf("Seed = %u\n", seed); + srandom(seed); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_RUNNER_TEARDOWN(PARC_TreeRedBlack) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestRunner_GetName(testRunner), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Remove_Ordered); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Create); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Insert_Destroy); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Insert_Ordered); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Insert_OutOfOrder); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Insert_Overwrite); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_DestroyTillEmpty); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Size_Empty); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Size); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Size_Overwrite); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Get_EmptyTree); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Get_NonExistent); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Get_First); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Get); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Get_Last); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Get_Biggest); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Get_Smallest); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Get_FirstKey); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Get_LastKey); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Get_FirstKey_Empty); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Get_LastKey_Empty); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Remove_First); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Remove); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Remove_Last); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Remove_NonExistent); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_RemoveAndDestroy_First); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_RemoveAndDestroy); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_RemoveAndDestroy_Last); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_RemoveAndDestroy_NonExistent); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Remove_WithSuccessorNonRoot); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Remove_LeftChildRightChild); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Keys); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Values); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Equals_Empty); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Equals); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Equals_Func); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Equals_DifferentLength); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Equals_Not_Values); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Equals_Not_Values_Func); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Equals_Not_Keys); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Copy); + LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_Copy_Direct); + //LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_ExerciseRandom); + //LONGBOW_RUN_TEST_CASE(Global, PARC_TreeRedBlack_ExerciseRootFailure); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + /* + * uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + * if (outstandingAllocations != 0) { + * printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + * return LONGBOW_STATUS_MEMORYLEAK; + * } + */ + return LONGBOW_STATUS_SUCCEEDED; +} + +static +int +strComp(const void *s1, const void *s2) +{ + return strcmp((char *) s1, (char *) s2); +} + +static +bool +strEquals(const void *s1, const void *s2) +{ + return strcmp(s1, s2) == 0; +} + +static +int +recursiveCheckBlackDepth(const PARCTreeRedBlack *tree, const Node *node) +{ + assertNotNull(tree, "Null tree?\n"); + assertNotNull(node, "Null node?\n"); + if (node == tree->nil) { + return 0; + } + int right_depth = recursiveCheckBlackDepth(tree, node->right_child); + int left_depth = recursiveCheckBlackDepth(tree, node->left_child); + assertTrue(right_depth == left_depth, "Wrong depth!!\n"); + if (_rbNodeColor(node) == BLACK) { + return right_depth + 1; + } + return right_depth; +} + +static +void +rbCheckTree(const PARCTreeRedBlack *tree) +{ + assertNotNull(tree, "Tree can't be NULL"); + //printf("--- TREE ---\n"); + _rbNodeAssertTreeInvariants(tree); + if (tree->size > 0) { + //_rbNodeRecursiveRun((PARCTreeRedBlack *)tree,tree->root,rbCheckNode,(void *)tree); + recursiveCheckBlackDepth(tree, tree->root); + } +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Remove_Ordered) +{ + char *insertList[16] = { + "01", + "02", + "03", + "04", + "05", + "06", + "07", + "08", + "09", + "10", + "11", + "12", + "13", + "14", + "15", + "16" + }; + + PARCTreeRedBlack *tree1; + + tree1 = parcTreeRedBlack_Create(strComp, NULL, NULL, strEquals, NULL, NULL); + + for (int i = 0; i < 16; i++) { + parcTreeRedBlack_Insert(tree1, insertList[i], insertList[i]); + } + + for (int i = 0; i < 14; i++) { + //rbPrintTreeString(tree1); + void *data = parcTreeRedBlack_Remove(tree1, insertList[i]); + assertNotNull(data, "Data is null!"); + } + + parcTreeRedBlack_Destroy(&tree1); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Create) +{ + PARCTreeRedBlack *tree; + + tree = parcTreeRedBlack_Create(stringComp, NULL, NULL, NULL, NULL, NULL); + + parcTreeRedBlack_Destroy(&tree); + + tree = parcTreeRedBlack_Create(stringComp, keyFree, NULL, NULL, valueFree, NULL); + + parcTreeRedBlack_Destroy(&tree); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Insert_Destroy) +{ + PARCTreeRedBlack *tree; + + tree = parcTreeRedBlack_Create(stringComp, keyFree, NULL, NULL, valueFree, NULL); + + void *value1 = valueNew("value 1"); + void *key1 = keyNew("1"); + void *value2 = valueNew("value 2"); + void *key2 = keyNew("2"); + void *value3 = valueNew("value 3"); + void *key3 = keyNew("3"); + + parcTreeRedBlack_Insert(tree, key1, value1); + parcTreeRedBlack_Insert(tree, key2, value2); + parcTreeRedBlack_Insert(tree, key3, value3); + + parcTreeRedBlack_Destroy(&tree); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Insert_Overwrite) +{ + PARCTreeRedBlack *tree; + + tree = parcTreeRedBlack_Create(stringComp, keyFree, NULL, NULL, valueFree, NULL); + + parcTreeRedBlack_Insert(tree, keyNew("1"), valueNew("v1")); + parcTreeRedBlack_Insert(tree, keyNew("2"), valueNew("v2")); + parcTreeRedBlack_Insert(tree, keyNew("3"), valueNew("v3")); + parcTreeRedBlack_Insert(tree, keyNew("3"), valueNew("v4")); + parcTreeRedBlack_Insert(tree, keyNew("3"), valueNew("v5")); + + assertTrue(3 == parcTreeRedBlack_Size(tree), "Wrong size of tree should stay at 3"); + + parcTreeRedBlack_Destroy(&tree); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Insert_Ordered) +{ + PARCTreeRedBlack *tree; + + tree = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + + parcTreeRedBlack_Insert(tree, (void *) 1, (void *) 1001); + parcTreeRedBlack_Insert(tree, (void *) 2, (void *) 1002); + parcTreeRedBlack_Insert(tree, (void *) 3, (void *) 1003); + + parcTreeRedBlack_Destroy(&tree); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Insert_OutOfOrder) +{ + PARCTreeRedBlack *tree; + + tree = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + + parcTreeRedBlack_Insert(tree, (void *) 4, (void *) 1004); + parcTreeRedBlack_Insert(tree, (void *) 2, (void *) 1002); + parcTreeRedBlack_Insert(tree, (void *) 3, (void *) 1003); + parcTreeRedBlack_Insert(tree, (void *) 1, (void *) 1001); + + parcTreeRedBlack_Destroy(&tree); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Size_Empty) +{ + PARCTreeRedBlack *tree; + + tree = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + + assertTrue(0 == parcTreeRedBlack_Size(tree), "Wrong size of tree - empty, start"); + + parcTreeRedBlack_Destroy(&tree); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Size) +{ + PARCTreeRedBlack *tree; + size_t size; + + tree = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + + parcTreeRedBlack_Insert(tree, (void *) 4, (void *) 1004); + parcTreeRedBlack_Insert(tree, (void *) 2, (void *) 1002); + parcTreeRedBlack_Insert(tree, (void *) 3, (void *) 1003); + + assertTrue(3 == parcTreeRedBlack_Size(tree), "Wrong size of tree after add 3"); + + parcTreeRedBlack_Insert(tree, (void *) 1, (void *) 1001); + + assertTrue(4 == parcTreeRedBlack_Size(tree), "Wrong size of tree after add 1 more"); + + parcTreeRedBlack_RemoveAndDestroy(tree, (void *) 2); + + size = parcTreeRedBlack_Size(tree); + + assertTrue(3 == size, "Wrong size of tree after 1 delete (%zu instead of 3)", size); + + parcTreeRedBlack_Insert(tree, (void *) 7, (void *) 1007); + + assertTrue(4 == parcTreeRedBlack_Size(tree), "Wrong size of tree after add 1 more"); + + parcTreeRedBlack_RemoveAndDestroy(tree, (void *) 3); + assertTrue(3 == parcTreeRedBlack_Size(tree), "Wrong size of tree after del 1 more - 3"); + + parcTreeRedBlack_Destroy(&tree); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_DestroyTillEmpty) +{ + PARCTreeRedBlack *tree; + size_t size; + + tree = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + + parcTreeRedBlack_Insert(tree, (void *) 4, (void *) 1004); + parcTreeRedBlack_Insert(tree, (void *) 2, (void *) 1002); + parcTreeRedBlack_Insert(tree, (void *) 3, (void *) 1003); + parcTreeRedBlack_Insert(tree, (void *) 1, (void *) 1001); + parcTreeRedBlack_Insert(tree, (void *) 5, (void *) 1001); + parcTreeRedBlack_Insert(tree, (void *) 7, (void *) 1001); + parcTreeRedBlack_Insert(tree, (void *) 6, (void *) 1001); + + parcTreeRedBlack_RemoveAndDestroy(tree, (void *) 3); + parcTreeRedBlack_RemoveAndDestroy(tree, (void *) 1); + parcTreeRedBlack_RemoveAndDestroy(tree, (void *) 4); + parcTreeRedBlack_RemoveAndDestroy(tree, (void *) 2); + parcTreeRedBlack_RemoveAndDestroy(tree, (void *) 6); + parcTreeRedBlack_RemoveAndDestroy(tree, (void *) 5); + parcTreeRedBlack_RemoveAndDestroy(tree, (void *) 7); + + size = parcTreeRedBlack_Size(tree); + + assertTrue(0 == size, "Wrong size of tree - empty (got %zu)", size); + + parcTreeRedBlack_Destroy(&tree); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Size_Overwrite) +{ + PARCTreeRedBlack *tree; + + tree = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + + parcTreeRedBlack_Insert(tree, (void *) 4, (void *) 1004); + parcTreeRedBlack_Insert(tree, (void *) 2, (void *) 1002); + parcTreeRedBlack_Insert(tree, (void *) 3, (void *) 1003); + + // Size is 3 here, we'll insert the same number now.. + + parcTreeRedBlack_Insert(tree, (void *) 3, (void *) 1033); + + assertTrue(3 == parcTreeRedBlack_Size(tree), "Wrong size of tree after overwrite"); + + parcTreeRedBlack_Destroy(&tree); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Get_EmptyTree) +{ + PARCTreeRedBlack *tree; + + tree = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + + void *value = parcTreeRedBlack_Get(tree, (void *) 1); + + assertTrue(NULL == value, "Object did not exist, must return NULL"); + + parcTreeRedBlack_Destroy(&tree); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Get_NonExistent) +{ + PARCTreeRedBlack *tree; + + tree = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + + for (long i = 1; i < 10; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree, (void *) i, (void *) (i << 8)); + } + + void *value = parcTreeRedBlack_Get(tree, (void *) 100); + + assertTrue(NULL == value, "Object did not exist, must return NULL"); + + parcTreeRedBlack_Destroy(&tree); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Get_First) +{ + PARCTreeRedBlack *tree; + + tree = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + + for (long i = 1; i < 4; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree, (void *) i, (void *) (i << 8)); + } + + void *value = parcTreeRedBlack_Get(tree, (void *) 1); + + assertTrue((1 << 8) == (long) value, "Wrong value, got %ld", (long) value); + + parcTreeRedBlack_Destroy(&tree); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Get) +{ + PARCTreeRedBlack *tree; + + tree = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + + for (long i = 1; i < 10; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree, (void *) i, (void *) (i << 8)); + } + + void *value = parcTreeRedBlack_Get(tree, (void *) 4); + + assertTrue((4 << 8) == (long) value, "Wrong value, got %ld", (long) value); + + parcTreeRedBlack_Destroy(&tree); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Get_Last) +{ + PARCTreeRedBlack *tree; + + tree = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + + for (long i = 1; i < 10; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree, (void *) i, (void *) (i << 8)); + } + + void *value = parcTreeRedBlack_Get(tree, (void *) 9); + + assertTrue((9 << 8) == (long) value, "Wrong value, got %ld", (long) value); + + parcTreeRedBlack_Destroy(&tree); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Get_Smallest) +{ + PARCTreeRedBlack *tree; + + tree = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + + for (long i = 30; i < 40; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree, (void *) i, (void *) (i << 8)); + } + for (long i = 1; i < 10; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree, (void *) i, (void *) (i << 8)); + } + for (long i = 20; i < 30; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree, (void *) i, (void *) (i << 8)); + } + + + void *value = parcTreeRedBlack_Get(tree, (void *) 1); + + assertTrue((1 << 8) == (long) value, "Wrong value, got %ld", (long) value); + + parcTreeRedBlack_Destroy(&tree); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Get_Biggest) +{ + PARCTreeRedBlack *tree; + + tree = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + + for (long i = 30; i < 40; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree, (void *) i, (void *) (i << 8)); + } + for (long i = 1; i < 10; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree, (void *) i, (void *) (i << 8)); + } + for (long i = 20; i < 30; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree, (void *) i, (void *) (i << 8)); + } + + + void *value = parcTreeRedBlack_Get(tree, (void *) 39); + + assertTrue((39 << 8) == (long) value, "Wrong value, got %ld", (long) value); + + parcTreeRedBlack_Destroy(&tree); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Get_FirstKey) +{ + PARCTreeRedBlack *tree; + + tree = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + + for (long i = 30; i < 40; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree, (void *) i, (void *) (i << 8)); + } + for (long i = 1; i < 10; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree, (void *) i, (void *) (i << 8)); + } + for (long i = 20; i < 30; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree, (void *) i, (void *) (i << 8)); + } + + void *key = parcTreeRedBlack_FirstKey(tree); + + assertTrue(1 == (long) key, "Wrong value, got %ld", (long) key); + + parcTreeRedBlack_Destroy(&tree); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Get_FirstKey_Empty) +{ + PARCTreeRedBlack *tree; + + tree = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + + void *key = parcTreeRedBlack_FirstKey(tree); + + assertNull(key, "Should get NULL on empty tree"); + + parcTreeRedBlack_Destroy(&tree); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Get_LastKey_Empty) +{ + PARCTreeRedBlack *tree; + + tree = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + + void *key = parcTreeRedBlack_LastKey(tree); + + assertNull(key, "Should get NULL on empty tree"); + + parcTreeRedBlack_Destroy(&tree); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Get_LastKey) +{ + PARCTreeRedBlack *tree; + + tree = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + + for (long i = 30; i < 40; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree, (void *) i, (void *) (i << 8)); + } + for (long i = 1; i < 10; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree, (void *) i, (void *) (i << 8)); + } + for (long i = 20; i < 30; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree, (void *) i, (void *) (i << 8)); + } + + void *key = parcTreeRedBlack_LastKey(tree); + + assertTrue(39 == (long) key, "Wrong value, got %ld", (long) key); + + parcTreeRedBlack_Destroy(&tree); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Remove_First) +{ + PARCTreeRedBlack *tree1; + PARCTreeRedBlack *tree2; + + tree1 = parcTreeRedBlack_Create(intComp, keyFree, NULL, intEquals, valueFree, NULL); + tree2 = parcTreeRedBlack_Create(intComp, keyFree, NULL, intEquals, valueFree, NULL); + + for (int i = 30; i < 40; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree1, keyNewInt(i), valueNewInt(i << 8)); + parcTreeRedBlack_Insert(tree2, keyNewInt(i), valueNewInt(i << 8)); + } + + parcTreeRedBlack_Insert(tree1, keyNewInt(1), valueNewInt(1 << 8)); + + for (int i = 2; i < 10; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree1, keyNewInt(i), valueNewInt(i << 8)); + parcTreeRedBlack_Insert(tree2, keyNewInt(i), valueNewInt(i << 8)); + } + + for (int i = 20; i < 30; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree1, keyNewInt(i), valueNewInt(i << 8)); + parcTreeRedBlack_Insert(tree2, keyNewInt(i), valueNewInt(i << 8)); + } + + int searchKey = 1; + + void *data = parcTreeRedBlack_Remove(tree1, &searchKey); + + valueFree(&data); + + assertTrue(parcTreeRedBlack_Equals(tree1, tree2), "Trees dont match after remove"); + + parcTreeRedBlack_Destroy(&tree1); + parcTreeRedBlack_Destroy(&tree2); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Remove) +{ + PARCTreeRedBlack *tree1; + PARCTreeRedBlack *tree2; + + tree1 = parcTreeRedBlack_Create(intComp, keyFree, NULL, intEquals, valueFree, NULL); + tree2 = parcTreeRedBlack_Create(intComp, keyFree, NULL, intEquals, valueFree, NULL); + + for (int i = 31; i < 40; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree1, keyNewInt(i), valueNewInt(i << 8)); + parcTreeRedBlack_Insert(tree2, keyNewInt(i), valueNewInt(i << 8)); + } + + parcTreeRedBlack_Insert(tree1, keyNewInt(30), valueNewInt(31 << 8)); + + for (int i = 2; i < 10; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree1, keyNewInt(i), valueNewInt(i << 8)); + parcTreeRedBlack_Insert(tree2, keyNewInt(i), valueNewInt(i << 8)); + } + + for (int i = 20; i < 30; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree1, keyNewInt(i), valueNewInt(i << 8)); + parcTreeRedBlack_Insert(tree2, keyNewInt(i), valueNewInt(i << 8)); + } + + int searchKey = 30; + + void *data = parcTreeRedBlack_Remove(tree1, &searchKey); + + valueFree(&data); + + assertTrue(parcTreeRedBlack_Equals(tree1, tree2), "Trees dont match after remove"); + + parcTreeRedBlack_Destroy(&tree1); + parcTreeRedBlack_Destroy(&tree2); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Remove_Last) +{ + PARCTreeRedBlack *tree1; + PARCTreeRedBlack *tree2; + + tree1 = parcTreeRedBlack_Create(intComp, keyFree, NULL, intEquals, valueFree, NULL); + tree2 = parcTreeRedBlack_Create(intComp, keyFree, NULL, intEquals, valueFree, NULL); + + for (int i = 30; i < 40; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree1, keyNewInt(i), valueNewInt(i << 8)); + parcTreeRedBlack_Insert(tree2, keyNewInt(i), valueNewInt(i << 8)); + } + parcTreeRedBlack_Insert(tree1, keyNewInt(100), valueNewInt(100 << 8)); + for (int i = 2; i < 10; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree1, keyNewInt(i), valueNewInt(i << 8)); + parcTreeRedBlack_Insert(tree2, keyNewInt(i), valueNewInt(i << 8)); + } + for (int i = 20; i < 30; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree1, keyNewInt(i), valueNewInt(i << 8)); + parcTreeRedBlack_Insert(tree2, keyNewInt(i), valueNewInt(i << 8)); + } + + int searchKey = 100; + + void *data = parcTreeRedBlack_Remove(tree1, &searchKey); + + valueFree(&data); + + assertTrue(parcTreeRedBlack_Equals(tree1, tree2), "Trees dont match after remove"); + + parcTreeRedBlack_Destroy(&tree1); + parcTreeRedBlack_Destroy(&tree2); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_RemoveAndDestroy_First) +{ + PARCTreeRedBlack *tree1; + PARCTreeRedBlack *tree2; + + tree1 = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + tree2 = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + + for (long i = 30; i < 40; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree1, (void *) i, (void *) (i << 8)); + parcTreeRedBlack_Insert(tree2, (void *) i, (void *) (i << 8)); + } + + parcTreeRedBlack_Insert(tree1, (void *) 1, (void *) (1 << 8)); + + for (long i = 2; i < 10; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree1, (void *) i, (void *) (i << 8)); + parcTreeRedBlack_Insert(tree2, (void *) i, (void *) (i << 8)); + } + for (long i = 20; i < 30; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree1, (void *) i, (void *) (i << 8)); + parcTreeRedBlack_Insert(tree2, (void *) i, (void *) (i << 8)); + } + + parcTreeRedBlack_RemoveAndDestroy(tree1, (void *) 1); + + assertTrue(parcTreeRedBlack_Equals(tree1, tree2), "Trees dont match after remove"); + + parcTreeRedBlack_Destroy(&tree1); + parcTreeRedBlack_Destroy(&tree2); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_RemoveAndDestroy) +{ + PARCTreeRedBlack *tree1; + PARCTreeRedBlack *tree2; + + tree1 = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + tree2 = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + + for (long i = 31; i < 40; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree1, (void *) i, (void *) (i << 8)); + parcTreeRedBlack_Insert(tree2, (void *) i, (void *) (i << 8)); + } + + parcTreeRedBlack_Insert(tree1, (void *) 30, (void *) (30 << 8)); + + for (long i = 2; i < 10; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree1, (void *) i, (void *) (i << 8)); + parcTreeRedBlack_Insert(tree2, (void *) i, (void *) (i << 8)); + } + for (long i = 20; i < 30; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree1, (void *) i, (void *) (i << 8)); + parcTreeRedBlack_Insert(tree2, (void *) i, (void *) (i << 8)); + } + + parcTreeRedBlack_RemoveAndDestroy(tree1, (void *) 30); + + assertTrue(parcTreeRedBlack_Equals(tree1, tree2), "Trees dont match after remove"); + + parcTreeRedBlack_Destroy(&tree1); + parcTreeRedBlack_Destroy(&tree2); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Remove_NonExistent) +{ + PARCTreeRedBlack *tree1; + PARCTreeRedBlack *tree2; + + tree1 = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + tree2 = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + + for (long i = 30; i < 40; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree1, (void *) i, (void *) (i << 8)); + parcTreeRedBlack_Insert(tree2, (void *) i, (void *) (i << 8)); + } + for (long i = 2; i < 10; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree1, (void *) i, (void *) (i << 8)); + parcTreeRedBlack_Insert(tree2, (void *) i, (void *) (i << 8)); + } + for (long i = 20; i < 30; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree1, (void *) i, (void *) (i << 8)); + parcTreeRedBlack_Insert(tree2, (void *) i, (void *) (i << 8)); + } + + void *element = parcTreeRedBlack_Remove(tree1, (void *) 100); + + assertNull(element, "Return value must be NULL on non existing element"); + assertTrue(parcTreeRedBlack_Equals(tree1, tree2), "Trees dont match after remove"); + + parcTreeRedBlack_Destroy(&tree1); + parcTreeRedBlack_Destroy(&tree2); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_RemoveAndDestroy_NonExistent) +{ + PARCTreeRedBlack *tree1; + PARCTreeRedBlack *tree2; + + tree1 = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + tree2 = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + + for (long i = 30; i < 40; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree1, (void *) i, (void *) (i << 8)); + parcTreeRedBlack_Insert(tree2, (void *) i, (void *) (i << 8)); + } + for (long i = 2; i < 10; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree1, (void *) i, (void *) (i << 8)); + parcTreeRedBlack_Insert(tree2, (void *) i, (void *) (i << 8)); + } + for (long i = 20; i < 30; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree1, (void *) i, (void *) (i << 8)); + parcTreeRedBlack_Insert(tree2, (void *) i, (void *) (i << 8)); + } + + parcTreeRedBlack_RemoveAndDestroy(tree1, (void *) 100); + + assertTrue(parcTreeRedBlack_Equals(tree1, tree2), "Trees dont match after remove"); + + parcTreeRedBlack_Destroy(&tree1); + parcTreeRedBlack_Destroy(&tree2); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Remove_WithSuccessorNonRoot) +{ + PARCTreeRedBlack *tree1; + PARCTreeRedBlack *tree2; + + long insert1[15] = { 8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15 }; + long insert2[13] = { 8, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15 }; + + tree1 = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + tree2 = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + + + + for (int i = 0; i < 15; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree1, (void *) insert1[i], (void *) (insert1[i] << 8)); + } + + for (int i = 0; i < 13; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree2, (void *) insert2[i], (void *) (insert2[i] << 8)); + } + + parcTreeRedBlack_RemoveAndDestroy(tree1, (void *) 4); + parcTreeRedBlack_RemoveAndDestroy(tree1, (void *) 12); + + assertTrue(parcTreeRedBlack_Equals(tree1, tree2), "Trees dont match after remove"); + + parcTreeRedBlack_Destroy(&tree1); + parcTreeRedBlack_Destroy(&tree2); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Remove_LeftChildRightChild) +{ + PARCTreeRedBlack *tree1; + PARCTreeRedBlack *tree2; + + long insert1[15] = { 8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15 }; + long insert2[15] = { 8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15 }; + + tree1 = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + tree2 = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + + + + for (int i = 0; i < 15; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree1, (void *) insert1[i], (void *) (insert1[i] << 8)); + } + + for (int i = 0; i < 15; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree2, (void *) insert2[i], (void *) (insert2[i] << 8)); + } + + parcTreeRedBlack_RemoveAndDestroy(tree1, (void *) 13); + parcTreeRedBlack_RemoveAndDestroy(tree1, (void *) 7); + parcTreeRedBlack_RemoveAndDestroy(tree1, (void *) 14); + parcTreeRedBlack_RemoveAndDestroy(tree1, (void *) 6); + parcTreeRedBlack_RemoveAndDestroy(tree1, (void *) 15); + parcTreeRedBlack_RemoveAndDestroy(tree1, (void *) 12); + parcTreeRedBlack_RemoveAndDestroy(tree1, (void *) 11); + parcTreeRedBlack_RemoveAndDestroy(tree1, (void *) 10); + parcTreeRedBlack_RemoveAndDestroy(tree1, (void *) 9); + parcTreeRedBlack_RemoveAndDestroy(tree1, (void *) 8); + parcTreeRedBlack_RemoveAndDestroy(tree1, (void *) 5); + parcTreeRedBlack_RemoveAndDestroy(tree1, (void *) 4); + parcTreeRedBlack_RemoveAndDestroy(tree1, (void *) 3); + parcTreeRedBlack_RemoveAndDestroy(tree1, (void *) 2); + parcTreeRedBlack_RemoveAndDestroy(tree1, (void *) 1); + + parcTreeRedBlack_RemoveAndDestroy(tree2, (void *) 1); + parcTreeRedBlack_RemoveAndDestroy(tree2, (void *) 2); + parcTreeRedBlack_RemoveAndDestroy(tree2, (void *) 3); + parcTreeRedBlack_RemoveAndDestroy(tree2, (void *) 4); + parcTreeRedBlack_RemoveAndDestroy(tree2, (void *) 5); + parcTreeRedBlack_RemoveAndDestroy(tree2, (void *) 6); + parcTreeRedBlack_RemoveAndDestroy(tree2, (void *) 7); + parcTreeRedBlack_RemoveAndDestroy(tree2, (void *) 8); + parcTreeRedBlack_RemoveAndDestroy(tree2, (void *) 9); + parcTreeRedBlack_RemoveAndDestroy(tree2, (void *) 10); + parcTreeRedBlack_RemoveAndDestroy(tree2, (void *) 11); + parcTreeRedBlack_RemoveAndDestroy(tree2, (void *) 12); + parcTreeRedBlack_RemoveAndDestroy(tree2, (void *) 13); + parcTreeRedBlack_RemoveAndDestroy(tree2, (void *) 14); + parcTreeRedBlack_RemoveAndDestroy(tree2, (void *) 15); + + //assertTrue(parcTreeRedBlack_Equals(tree1,tree2),"Trees dont match after remove"); + + parcTreeRedBlack_Destroy(&tree1); + parcTreeRedBlack_Destroy(&tree2); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_RemoveAndDestroy_Last) +{ + PARCTreeRedBlack *tree1; + PARCTreeRedBlack *tree2; + + tree1 = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + tree2 = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + + for (long i = 30; i < 40; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree1, (void *) i, (void *) (i << 8)); + parcTreeRedBlack_Insert(tree2, (void *) i, (void *) (i << 8)); + } + parcTreeRedBlack_Insert(tree1, (void *) 100, (void *) (100 << 8)); + for (long i = 2; i < 10; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree1, (void *) i, (void *) (i << 8)); + parcTreeRedBlack_Insert(tree2, (void *) i, (void *) (i << 8)); + } + for (long i = 20; i < 30; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree1, (void *) i, (void *) (i << 8)); + parcTreeRedBlack_Insert(tree2, (void *) i, (void *) (i << 8)); + } + + parcTreeRedBlack_RemoveAndDestroy(tree1, (void *) 100); + + assertTrue(parcTreeRedBlack_Equals(tree1, tree2), "Trees dont match after remove"); + + parcTreeRedBlack_Destroy(&tree1); + parcTreeRedBlack_Destroy(&tree2); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Keys) +{ + PARCTreeRedBlack *tree1; + PARCArrayList *list; + + tree1 = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + list = parcArrayList_Create(NULL); + + // Insert in tree out of order + for (long i = 10; i < 20; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree1, (void *) i, (void *) (i << 8)); + } + for (long i = 1; i < 10; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree1, (void *) i, (void *) (i << 8)); + } + + // Insert in list in order + for (long i = 1; i < 20; i++) { + // Add some elements to the tree + parcArrayList_Add(list, (void *) i); + } + + PARCArrayList *keys = parcTreeRedBlack_Keys(tree1); + + assertTrue(parcArrayList_Equals(list, keys), "Key list doesnt' match"); + + parcArrayList_Destroy(&keys); + parcArrayList_Destroy(&list); + parcTreeRedBlack_Destroy(&tree1); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Values) +{ + PARCTreeRedBlack *tree1; + PARCArrayList *list; + + tree1 = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + list = parcArrayList_Create(NULL); + + // Insert in tree out of order + for (long i = 10; i < 20; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree1, (void *) i, (void *) (i << 8)); + } + for (long i = 1; i < 10; i++) { + // Add some elements to the tree + parcTreeRedBlack_Insert(tree1, (void *) i, (void *) (i << 8)); + } + + // Insert in list in order + for (long i = 1; i < 20; i++) { + // Add some elements to the tree + parcArrayList_Add(list, (void *) (i << 8)); + } + + PARCArrayList *values = parcTreeRedBlack_Values(tree1); + + assertTrue(parcArrayList_Equals(list, values), "Key list doesnt' match"); + + parcArrayList_Destroy(&values); + parcArrayList_Destroy(&list); + parcTreeRedBlack_Destroy(&tree1); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Equals_Empty) +{ + PARCTreeRedBlack *tree1; + PARCTreeRedBlack *tree2; + + tree1 = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + tree2 = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + + assertTrue(parcTreeRedBlack_Equals(tree1, tree2), "Empty lists are not equal"); + + parcTreeRedBlack_Destroy(&tree1); + parcTreeRedBlack_Destroy(&tree2); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Equals_DifferentLength) +{ + PARCTreeRedBlack *tree1; + PARCTreeRedBlack *tree2; + + int compareInserts = 100; + + tree1 = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + tree2 = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + + for (long i = 1; i < compareInserts; i++) { + parcTreeRedBlack_Insert(tree1, + (void *) i, + (void *) (i << 8)); + parcTreeRedBlack_Insert(tree2, + (void *) (compareInserts - i), + (void *) ((compareInserts - i) << 8)); + } + parcTreeRedBlack_Insert(tree2, (void *) (1000), (void *) ((12304) << 8)); + + assertFalse(parcTreeRedBlack_Equals(tree1, tree2), "Lists are equal"); + + parcTreeRedBlack_Destroy(&tree1); + parcTreeRedBlack_Destroy(&tree2); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Equals_Not_Values) +{ + PARCTreeRedBlack *tree1; + PARCTreeRedBlack *tree2; + + int compareInserts = 100; + + tree1 = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + tree2 = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + + for (long i = 1; i < compareInserts; i++) { + parcTreeRedBlack_Insert(tree1, + (void *) i, + (void *) (i << 8)); + parcTreeRedBlack_Insert(tree2, + (void *) (compareInserts - i), + (void *) ((compareInserts + i) << 8)); + } + + assertFalse(parcTreeRedBlack_Equals(tree1, tree2), "Lists are equal"); + + parcTreeRedBlack_Destroy(&tree1); + parcTreeRedBlack_Destroy(&tree2); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Equals_Not_Values_Func) +{ + PARCTreeRedBlack *tree1; + PARCTreeRedBlack *tree2; + + int compareInserts = 100; + + tree1 = parcTreeRedBlack_Create(pointerComp, keyFree, keyCopy, intEquals, valueFree, valueCopy); + tree2 = parcTreeRedBlack_Create(pointerComp, keyFree, keyCopy, intEquals, valueFree, valueCopy); + + for (int i = 1; i < compareInserts; i++) { + parcTreeRedBlack_Insert(tree1, + keyNewInt(i), + valueNewInt(i + 1000)); + parcTreeRedBlack_Insert(tree2, + keyNewInt(i), + valueNewInt(i + 2000)); + } + + assertFalse(parcTreeRedBlack_Equals(tree1, tree2), "Lists are equal"); + + parcTreeRedBlack_Destroy(&tree1); + parcTreeRedBlack_Destroy(&tree2); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Equals_Not_Keys) +{ + PARCTreeRedBlack *tree1; + PARCTreeRedBlack *tree2; + + int compareInserts = 100; + + tree1 = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + tree2 = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + + for (long i = 1; i < compareInserts; i++) { + parcTreeRedBlack_Insert(tree1, + (void *) i, + (void *) (i << 8)); + parcTreeRedBlack_Insert(tree2, + (void *) (compareInserts + i), + (void *) ((compareInserts - i) << 8)); + } + + assertFalse(parcTreeRedBlack_Equals(tree1, tree2), "Lists are equal"); + + parcTreeRedBlack_Destroy(&tree1); + parcTreeRedBlack_Destroy(&tree2); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Equals) +{ + PARCTreeRedBlack *tree1; + PARCTreeRedBlack *tree2; + + int compareInserts = 100; + + tree1 = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + tree2 = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + + for (long i = 1; i < compareInserts; i++) { + parcTreeRedBlack_Insert(tree1, + (void *) i, + (void *) (i << 8)); + parcTreeRedBlack_Insert(tree2, + (void *) (compareInserts - i), + (void *) ((compareInserts - i) << 8)); + } + + assertTrue(parcTreeRedBlack_Equals(tree1, tree2), "Lists are not equal"); + + parcTreeRedBlack_Destroy(&tree1); + parcTreeRedBlack_Destroy(&tree2); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Equals_Func) +{ + PARCTreeRedBlack *tree1; + PARCTreeRedBlack *tree2; + + int compareInserts = 100; + + tree1 = parcTreeRedBlack_Create(intComp, keyFree, keyCopy, intEquals, valueFree, valueCopy); + tree2 = parcTreeRedBlack_Create(intComp, keyFree, keyCopy, intEquals, valueFree, valueCopy); + + for (int i = 1; i < compareInserts; i++) { + parcTreeRedBlack_Insert(tree1, + keyNewInt(i), + valueNewInt(i + 1000)); + parcTreeRedBlack_Insert(tree2, + keyNewInt(i), + valueNewInt(i + 1000)); + } + + assertTrue(parcTreeRedBlack_Equals(tree1, tree2), "Lists are not equal"); + + parcTreeRedBlack_Destroy(&tree1); + parcTreeRedBlack_Destroy(&tree2); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Copy) +{ + PARCTreeRedBlack *tree1; + PARCTreeRedBlack *tree2; + + int compareInserts = 20; + + tree1 = parcTreeRedBlack_Create(intComp, keyFree, keyCopy, intEquals, valueFree, valueCopy); + + for (int i = 1; i < compareInserts; i++) { + void *key = keyNewInt(i); + void *value = valueNewInt(i + 1000); + //value = (void *) &((*(int*)value) + 100); + parcTreeRedBlack_Insert(tree1, key, value); + } + + tree2 = parcTreeRedBlack_Copy(tree1); + + assertTrue(parcTreeRedBlack_Equals(tree1, tree2), "Lists are not equal"); + + parcTreeRedBlack_Destroy(&tree1); + parcTreeRedBlack_Destroy(&tree2); +} + +LONGBOW_TEST_CASE(Global, PARC_TreeRedBlack_Copy_Direct) +{ + PARCTreeRedBlack *tree1; + PARCTreeRedBlack *tree2; + + int compareInserts = 20; + + tree1 = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + + for (long i = 1; i < compareInserts; i++) { + parcTreeRedBlack_Insert(tree1, + (void *) i, + (void *) (i << 8)); + } + + tree2 = parcTreeRedBlack_Copy(tree1); + + assertTrue(parcTreeRedBlack_Equals(tree1, tree2), "Lists are not equal"); + + parcTreeRedBlack_Destroy(&tree1); + parcTreeRedBlack_Destroy(&tree2); +} + +LONGBOW_TEST_FIXTURE(Local) +{ + //LONGBOW_RUN_TEST_CASE(Local, PARC_TreeRedBlack_EnsureRemaining_NonEmpty); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Stress) +{ + // LongBow could use a command line option to enable/disable tests + // See LongBow issue #5 + if (getenv("LongBowStress")) { + LONGBOW_RUN_TEST_CASE(Stress, PARC_TreeRedBlack_ExerciseRandomSeededSmall); + LONGBOW_RUN_TEST_CASE(Stress, PARC_TreeRedBlack_ExerciseRandomSeeded); + } +} + +LONGBOW_TEST_FIXTURE_SETUP(Stress) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Stress) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Stress, PARC_TreeRedBlack_ExerciseRandomSeededSmall) +{ + PARCTreeRedBlack *tree1; + unsigned seed; + char *seedString; + + seedString = getenv("RBSeed"); + if (seedString) { + seed = (unsigned) atol(seedString); + } else { + seed = 4179329122; // known to fail + } + + for (int j = 0; j < 1; j++) { + // this test case should obtain a seed and number of iterations from a + // command line option once LongBow has that feature available + + printf("Random seed %u\n", seed); + + srandom(seed); + tree1 = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + + int inserts = 0; + int deletes = 0; + + for (int i = 0; i < 100; i++) { + intptr_t item = 1 + (random() % 100); + int operation = random() % 1000; + if (operation < 400) { + inserts++; + parcTreeRedBlack_Insert(tree1, (void *) item, (void *) (item << 8)); + } else { + deletes++; + parcTreeRedBlack_Remove(tree1, (void *) item); + } + rbCheckTree(tree1); + } + + parcTreeRedBlack_Destroy(&tree1); + } +} + +LONGBOW_TEST_CASE(Stress, PARC_TreeRedBlack_ExerciseRandomSeeded) +{ + PARCTreeRedBlack *tree1; + unsigned seed; + char *seedString; + + // this test case should obtain a seed and number of iterations from a + // command line option once LongBow has that feature available + seedString = getenv("RBSeed"); + if (seedString) { + seed = (unsigned) atol(seedString); + } else { + seed = 4179329122; // known to fail + } + printf("Random seed %u\n", seed); + + srandom(seed); + + tree1 = parcTreeRedBlack_Create(pointerComp, NULL, NULL, NULL, NULL, NULL); + + int inserts = 0; + int deletes = 0; + + for (int i = 0; i < 100000; i++) { + intptr_t item = 1 + (random() % 10000); + int operation = random() % 1000; + if (operation < 400) { + inserts++; + parcTreeRedBlack_Insert(tree1, (void *) item, (void *) (item << 8)); + } else { + deletes++; + parcTreeRedBlack_Remove(tree1, (void *) item); + } + rbCheckTree(tree1); + } + + parcTreeRedBlack_Destroy(&tree1); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(PARC_TreeRedBlack); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_URI.c b/libparc/parc/algol/test/test_parc_URI.c new file mode 100644 index 00000000..5050deb3 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_URI.c @@ -0,0 +1,642 @@ +/* + * 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 "../parc_URI.c" +#include <LongBow/unit-test.h> + +#include <stdint.h> + +#include <parc/algol/parc_Hash.h> +#include <parc/algol/parc_SafeMemory.h> +#include <parc/testing/parc_ObjectTesting.h> + +#include <parc/algol/parc_Hash.h> + +#include "_test_parc_URI.h" + +LONGBOW_TEST_RUNNER(parcURI) +{ + LONGBOW_RUN_TEST_FIXTURE(parcURI); +} + +LONGBOW_TEST_RUNNER_SETUP(parcURI) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_RUNNER_TEARDOWN(parcURI) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("Tests leak memory by %d allocations\n", outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(parcURI) +{ + LONGBOW_RUN_TEST_CASE(parcURI, parseScheme); + LONGBOW_RUN_TEST_CASE(parcURI, parseScheme_Only); + LONGBOW_RUN_TEST_CASE(parcURI, parseScheme_BadScheme); + LONGBOW_RUN_TEST_CASE(parcURI, parseScheme_EmptyScheme); + + LONGBOW_RUN_TEST_CASE(parcURI, parseAuthority); + LONGBOW_RUN_TEST_CASE(parcURI, parseAuthority_NoAuthority); + LONGBOW_RUN_TEST_CASE(parcURI, parseAuthority_NoPath); + + LONGBOW_RUN_TEST_CASE(parcURI, parcURI_Acquire); + + LONGBOW_RUN_TEST_CASE(parcURI, parcURI_AuthorityEquals_SamePointer); + LONGBOW_RUN_TEST_CASE(parcURI, parcURI_AuthorityEquals_NullPointers); + + LONGBOW_RUN_TEST_CASE(parcURI, parcURI_QueryEquals_SamePointer); + LONGBOW_RUN_TEST_CASE(parcURI, parcURI_QueryEquals_NullPointers); + + LONGBOW_RUN_TEST_CASE(parcURI, parcURI_FragmentEquals_SamePointer); + LONGBOW_RUN_TEST_CASE(parcURI, parcURI_FragmentEquals_NullPointers); + + LONGBOW_RUN_TEST_CASE(parcURI, parcURI_SchemeEquals_SamePointer); + LONGBOW_RUN_TEST_CASE(parcURI, parcURI_SchemeEquals_NullPointers); + + LONGBOW_RUN_TEST_CASE(parcURI, parcURI_Parse); + LONGBOW_RUN_TEST_CASE(parcURI, parcURI_Parse_NoScheme); + LONGBOW_RUN_TEST_CASE(parcURI, parcURI_Parse_SchemeOnly); + + LONGBOW_RUN_TEST_CASE(parcURI, parcURI_SetScheme); + LONGBOW_RUN_TEST_CASE(parcURI, parcURI_SetScheme_Reset); + LONGBOW_RUN_TEST_CASE(parcURI, parcURI_SetScheme_Resetting); + + LONGBOW_RUN_TEST_CASE(parcURI, parcURI_SetFragment); + LONGBOW_RUN_TEST_CASE(parcURI, parcURI_SetFragment_Reset); + LONGBOW_RUN_TEST_CASE(parcURI, parcURI_SetFragment_Resetting); + + LONGBOW_RUN_TEST_CASE(parcURI, parcURI_SetQuery); + LONGBOW_RUN_TEST_CASE(parcURI, parcURI_SetQuery_Reset); + LONGBOW_RUN_TEST_CASE(parcURI, parcURI_SetQuery_Resetting); + + LONGBOW_RUN_TEST_CASE(parcURI, parcURI_SetAuthority); + LONGBOW_RUN_TEST_CASE(parcURI, parcURI_SetAuthority_Reset); + LONGBOW_RUN_TEST_CASE(parcURI, parcURI_SetAuthority_Resetting); + + LONGBOW_RUN_TEST_CASE(parcURI, parcURI_Equals_Contract); + + LONGBOW_RUN_TEST_CASE(parcURI, parcURI_GetPath); + LONGBOW_RUN_TEST_CASE(parcURI, parcURI_GetQuery); + LONGBOW_RUN_TEST_CASE(parcURI, parcURI_GetFragment); + LONGBOW_RUN_TEST_CASE(parcURI, parcURI_Copy); + LONGBOW_RUN_TEST_CASE(parcURI, parcURI_ToString_Full); + + LONGBOW_RUN_TEST_CASE(parcURI, PARCURI_ToString_SchemeOnly); + LONGBOW_RUN_TEST_CASE(parcURI, PARCURI_ToString_NoAuthority); +} + +LONGBOW_TEST_FIXTURE_SETUP(parcURI) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(parcURI) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(parcURI, parcURI_Acquire) +{ + char *uriString = URI_SCHEME "://" URI_AUTHORITY "/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?" URI_QUERY "#" URI_FRAGMENT; + + uriString = parcMemory_StringDuplicate(uriString, strlen(uriString)); + + PARCURI *uri = parcURI_Parse(uriString); + + PARCURI *handle = parcURI_Acquire(uri); + + assertTrue(parcURI_Equals(uri, handle), "Expected URI and acquired handle to be equal"); + + parcURI_Release(&handle); + parcURI_Release(&uri); + parcMemory_Deallocate((void **) &uriString); +} + +LONGBOW_TEST_CASE(parcURI, parcURI_AuthorityEquals_SamePointer) +{ + char *authority = "authority@auth"; + assertTrue(_parcURI_AuthorityEquals(authority, authority), + "Expected authorities to be equal since they're the same pointers: %p - %p", + (void *) authority, (void *) authority); +} + +LONGBOW_TEST_CASE(parcURI, parcURI_AuthorityEquals_NullPointers) +{ + char *authority = "authority@auth"; + assertFalse(_parcURI_AuthorityEquals(NULL, authority), "Expected authorities to not be equal since one is NULL"); + assertFalse(_parcURI_AuthorityEquals(authority, NULL), "Expected authorities to not be equal since one is NULL"); + assertTrue(_parcURI_AuthorityEquals(NULL, NULL), "Expected authorities to be equal since both are NULL"); +} + +LONGBOW_TEST_CASE(parcURI, parcURI_QueryEquals_SamePointer) +{ + char *query = "query"; + assertTrue(_parcURI_QueryEquals(query, query), + "Expected queries to be equal since they're the same pointers: %p - %p", + (void *) query, (void *) query); +} + +LONGBOW_TEST_CASE(parcURI, parcURI_QueryEquals_NullPointers) +{ + char *query = "query"; + assertFalse(_parcURI_QueryEquals(NULL, query), "Expected queries to not be equal since one is NULL"); + assertFalse(_parcURI_QueryEquals(query, NULL), "Expected queries to not be equal since one is NULL"); + assertTrue(_parcURI_QueryEquals(NULL, NULL), "Expected queries to be equal since both are NULL"); +} + +LONGBOW_TEST_CASE(parcURI, parcURI_FragmentEquals_SamePointer) +{ + char *fragment = "fragment"; + assertTrue(_parcURI_FragmentEquals(fragment, fragment), + "Expected fragments to be equal since they're the same pointers: %p - %p", + (void *) fragment, (void *) fragment); +} + +LONGBOW_TEST_CASE(parcURI, parcURI_FragmentEquals_NullPointers) +{ + char *fragment = "fragment"; + assertFalse(_parcURI_FragmentEquals(NULL, fragment), "Expected fragments to not be equal since one is NULL"); + assertFalse(_parcURI_FragmentEquals(fragment, NULL), "Expected fragments to not be equal since one is NULL"); + assertTrue(_parcURI_FragmentEquals(NULL, NULL), "Expected fragments to be equal since both are NULL"); +} + +LONGBOW_TEST_CASE(parcURI, parcURI_SchemeEquals_SamePointer) +{ + char *scheme = "scheme"; + assertTrue(_parcURI_SchemeEquals(scheme, scheme), + "Expected schemes to be equal since they're the same pointers: %p - %p", + (void *) scheme, (void *) scheme); +} + +LONGBOW_TEST_CASE(parcURI, parcURI_SchemeEquals_NullPointers) +{ + char *scheme = "scheme"; + assertFalse(_parcURI_SchemeEquals(NULL, scheme), "Expected schemes to not be equal since one is NULL"); + assertFalse(_parcURI_SchemeEquals(scheme, NULL), "Expected schemes to not be equal since one is NULL"); + assertTrue(_parcURI_SchemeEquals(NULL, NULL), "Expected schemes to be equal since both are NULL"); +} + +LONGBOW_TEST_CASE(parcURI, parseScheme_EmptyScheme) +{ + const char *pointer; + char *actual = _parseScheme(":", &pointer); // empty string + assertNull(actual, "Parsed scheme should be NULL since the input string was empty."); + actual = _parseScheme("", &pointer); // empty string + assertNull(actual, "Parsed scheme should be NULL since the input string was empty."); +} + +LONGBOW_TEST_CASE(parcURI, parcURI_Parse) +{ + char *uriString = URI_SCHEME "://" URI_AUTHORITY "/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?" URI_QUERY "#" URI_FRAGMENT; + + uriString = parcMemory_StringDuplicate(uriString, strlen(uriString)); + + PARCURI *uri = parcURI_Parse(uriString); + assertNotNull(uri, "Expected non-null result for '%s'", uriString); + + memset(uriString, 0, strlen(uriString)); + + assertTrue(strcmp(URI_SCHEME, parcURI_GetScheme(uri)) == 0, + "Expected '%s', actual '%s'", URI_SCHEME, parcURI_GetScheme(uri)); + assertTrue(strcmp(URI_AUTHORITY, parcURI_GetAuthority(uri)) == 0, + "Expected '%s', actual '%s'", URI_AUTHORITY, parcURI_GetAuthority(uri)); + assertTrue(strcmp(URI_QUERY, parcURI_GetQuery(uri)) == 0, + "Expected '%s', actual '%s'", URI_QUERY, parcURI_GetQuery(uri)); + assertTrue(strcmp(URI_FRAGMENT, parcURI_GetFragment(uri)) == 0, + "Expected '%s', actual '%s'", URI_FRAGMENT, parcURI_GetFragment(uri)); + + parcMemory_Deallocate((void **) &uriString); + + parcURI_Release(&uri); + assertNull(uri, "Expected parcURI_Release to null the pointer."); +} + +LONGBOW_TEST_CASE(parcURI, parcURI_Parse_NoScheme) +{ + char *uriString = "/" URI_PATH_SEGMENT; + + uriString = parcMemory_StringDuplicate(uriString, strlen(uriString)); + + PARCURI *uri = parcURI_Parse(uriString); + assertNull(uri, + "Expected null result for '%s'", uriString); + parcMemory_Deallocate((void **) &uriString); +} + +LONGBOW_TEST_CASE(parcURI, parcURI_Parse_SchemeOnly) +{ + char *uriString = URI_SCHEME ":"; + + uriString = parcMemory_StringDuplicate(uriString, strlen(uriString)); + + PARCURI *uri = parcURI_Parse(uriString); + assertNotNull(uri, + "Expected non-null result for '%s'", uriString); + + memset(uriString, 0, strlen(uriString)); + + assertTrue(strcmp(URI_SCHEME, parcURI_GetScheme(uri)) == 0, + "Expected '%s', actual '%s'", URI_SCHEME, parcURI_GetScheme(uri)); + assertNull(parcURI_GetAuthority(uri), + "Expected NULL, actual '%s'", parcURI_GetAuthority(uri)); + assertNull(parcURI_GetQuery(uri), + "Expected NULL, actual '%s'", parcURI_GetQuery(uri)); + assertNull(parcURI_GetFragment(uri), + "Expected NULL, actual '%s'", parcURI_GetFragment(uri)); + + parcMemory_Deallocate((void **) &uriString); + parcURI_Release(&uri); + assertNull(uri, + "Expected parcURI_Release to null the pointer."); +} + +LONGBOW_TEST_CASE(parcURI, parseScheme) +{ + const char *pointer; + char *actual = _parseScheme(URI_FULL, &pointer); + assertTrue(strcmp(URI_SCHEME, actual) == 0, + "Expected '%s' actual '%s'", URI_SCHEME, actual); + parcMemory_Deallocate((void **) &actual); +} + +LONGBOW_TEST_CASE(parcURI, parseScheme_Only) +{ + const char *pointer; + char *actual = _parseScheme(URI_SCHEME ":", &pointer); + assertTrue(strcmp(URI_SCHEME, actual) == 0, + "Expected '%s' actual '%s'", URI_SCHEME, actual); + parcMemory_Deallocate((void **) &actual); +} + +LONGBOW_TEST_CASE(parcURI, parseScheme_BadScheme) +{ + const char *pointer; + char *actual = _parseScheme("", &pointer); + assertNull(actual, + "Expected NULL actual '%s'", actual); +} + +LONGBOW_TEST_CASE(parcURI, PARCURI_GetAuthority) +{ + PARCURI *uri = parcURI_Parse(URI_SCHEME "://" URI_AUTHORITY "/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?" URI_QUERY "#" URI_FRAGMENT); + const char *actual = parcURI_GetAuthority(uri); + assertTrue(strcmp(TEST_URI_AUTHORITY, actual) == 0, + "Expected '%s' actual '%s'", URI_AUTHORITY, actual); + parcURI_Release(&uri); +} + +LONGBOW_TEST_CASE(parcURI, parcURI_GetQuery) +{ + PARCURI *uri = parcURI_Parse(URI_SCHEME "://" URI_AUTHORITY "/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?" URI_QUERY "#" URI_FRAGMENT); + const char *actual = parcURI_GetQuery(uri); + assertTrue(strcmp(URI_QUERY, actual) == 0, + "Expected '%s' actual '%s'", URI_FRAGMENT, actual); + + parcURI_Release(&uri); +} + +LONGBOW_TEST_CASE(parcURI, parcURI_GetFragment) +{ + PARCURI *uri = parcURI_Parse(URI_SCHEME "://" URI_AUTHORITY "/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?" URI_QUERY "#" URI_FRAGMENT); + const char *actual = parcURI_GetFragment(uri); + assertTrue(strcmp(URI_FRAGMENT, actual) == 0, + "Expected '%s' actual '%s'", URI_FRAGMENT, actual); + parcURI_Release(&uri); +} + +LONGBOW_TEST_CASE(parcURI, parcURI_GetPath) +{ + char *uriString = URI_SCHEME "://" URI_AUTHORITY "/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?" URI_QUERY "#" URI_FRAGMENT; + PARCURI *uri = parcURI_Parse(uriString); + PARCURIPath *actual = parcURI_GetPath(uri); + + char *string = parcURIPath_ToString(actual); + + char *expected = URI_PATH_SEGMENT "/" URI_PATH_SEGMENT; + assertTrue(strcmp(expected, string) == 0, "Expected '%s' actual '%s'", expected, string); + + parcMemory_Deallocate((void **) &string); + parcURI_Release(&uri); +} + +LONGBOW_TEST_CASE(parcURI, parcURI_Copy) +{ + PARCURI *uri = parcURI_Parse(URI_SCHEME "://" URI_AUTHORITY "/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?" URI_QUERY "#" URI_FRAGMENT); + PARCURI *uri2 = parcURI_Copy(uri); + + char *expected = parcURI_ToString(uri); + char *actual = parcURI_ToString(uri2); + + assertTrue(strcmp(expected, actual) == 0, + "Expected '%s' actual '%s'", expected, actual); + parcMemory_Deallocate((void **) &expected); + parcMemory_Deallocate((void **) &actual); + parcURI_Release(&uri); + parcURI_Release(&uri2); +} + +LONGBOW_TEST_CASE(parcURI, parcURI_Equals_Contract) +{ + PARCURI *x = parcURI_Parse(URI_SCHEME "://" URI_AUTHORITY "/" URI_PATH_SEGMENT "?" URI_QUERY "#" URI_FRAGMENT); + PARCURI *y = parcURI_Parse(URI_SCHEME "://" URI_AUTHORITY "/" URI_PATH_SEGMENT "?" URI_QUERY "#" URI_FRAGMENT); + PARCURI *z = parcURI_Parse(URI_SCHEME "://" URI_AUTHORITY "/" URI_PATH_SEGMENT "?" URI_QUERY "#" URI_FRAGMENT); + PARCURI *u = parcURI_Parse(URI_SCHEME "://" URI_AUTHORITY "/" URI_PATH_SEGMENT "?" URI_QUERY); + + parcObjectTesting_AssertEqualsFunction(parcURI_Equals, x, y, z, u); + + parcURI_Release(&x); + parcURI_Release(&y); + parcURI_Release(&z); + parcURI_Release(&u); +} + +LONGBOW_TEST_CASE(parcURI, PARCURI_GetScheme) +{ + PARCURI *uri = parcURI_Parse(URI_SCHEME "://" URI_AUTHORITY "/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?" URI_QUERY "#" URI_FRAGMENT); + const char *actual = parcURI_GetScheme(uri); + assertTrue(strcmp(TEST_URI_SCHEME, actual) == 0, + "Expected '%s' actual '%s'", TEST_URI_SCHEME, actual); + parcURI_Release(&uri); +} + +LONGBOW_TEST_CASE(parcURI, PARCURI_Parse) +{ + PARCURI *uri = parcURI_Parse(URI_SCHEME "://" URI_AUTHORITY "/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?" URI_QUERY "#" URI_FRAGMENT); + assertNotNull(uri, + "Expected a non-null result."); + + assertTrue(strcmp(URI_SCHEME, parcURI_GetScheme(uri)) == 0, + "Expected '%s', actual '%s'", URI_SCHEME, parcURI_GetScheme(uri)); + assertTrue(strcmp(URI_AUTHORITY, parcURI_GetAuthority(uri)) == 0, + "Expected '%s', actual '%s'", URI_AUTHORITY, parcURI_GetAuthority(uri)); + assertTrue(strcmp(URI_QUERY, parcURI_GetQuery(uri)) == 0, + "Expected '%s', actual '%s'", URI_QUERY, parcURI_GetQuery(uri)); + assertTrue(strcmp(URI_FRAGMENT, parcURI_GetFragment(uri)) == 0, + "Expected '%s', actual '%s'", URI_FRAGMENT, parcURI_GetFragment(uri)); + + parcURI_Release(&uri); +} + +LONGBOW_TEST_CASE(parcURI, parcURI_ToString_Full) +{ + char *expected = URI_SCHEME "://" URI_AUTHORITY "/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?" URI_QUERY "#" URI_FRAGMENT; + + PARCURI *uri = parcURI_Parse(expected); + char *actual = parcURI_ToString(uri); + + assertTrue(strcmp(expected, actual) == 0, + "Expected '%s' actual '%s'", expected, actual); + parcMemory_Deallocate((void **) &actual); + parcURI_Release(&uri); +} + +LONGBOW_TEST_CASE(parcURI, PARCURI_ToString_SchemeOnly) +{ + char *expected = URI_SCHEME ":" "/"; + PARCURI *uri = parcURI_Parse(expected); + char *actual = parcURI_ToString(uri); + + assertTrue(strcmp(expected, actual) == 0, + "Expected '%s' actual '%s'", expected, actual); + parcURI_Release(&uri); + parcMemory_Deallocate((void **) &actual); +} + +LONGBOW_TEST_CASE(parcURI, PARCURI_ToString_NoAuthority) +{ + char *expected = URI_SCHEME ":" "/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?" URI_QUERY "#" URI_FRAGMENT; + PARCURI *uri = parcURI_Parse(expected); + char *actual = parcURI_ToString(uri); + + assertTrue(strcmp(expected, actual) == 0, + "Expected '%s' actual '%s'", expected, actual); + + parcURI_Release(&uri); + parcMemory_Deallocate((void **) &actual); +} + +LONGBOW_TEST_CASE(parcURI, parseAuthority) +{ + char *authority = "//" URI_AUTHORITY "/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT; + char *expected = URI_AUTHORITY; + + const char *pointer; + char *actual = _parseAuthority(authority, &pointer); + assertTrue(strcmp(expected, actual) == 0, + "Expected '%s' actual '%s'", expected, actual); + assertTrue(*pointer == '/', + "Expected '/' actual '%c'", *pointer); + parcMemory_Deallocate((void **) &actual); +} + +LONGBOW_TEST_CASE(parcURI, parseAuthority_NoAuthority) +{ + char *string = "/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT; + + const char *pointer; + char *actual = _parseAuthority(string, &pointer); + assertTrue(actual == NULL, + "Expected NULL actual '%s'", actual); + assertTrue(*pointer == '/', + "Expected '/' actual '%c'", *pointer); +} + +LONGBOW_TEST_CASE(parcURI, parcURI_SetScheme) +{ + char *scheme = "scheme"; + PARCURI *uri = parcURI_Create(); + _parcURI_SetScheme(uri, scheme); + + assertTrue(strcmp(scheme, parcURI_GetScheme(uri)) == 0, + "Expected %s actual %s", scheme, parcURI_GetScheme(uri)); + parcURI_Release(&uri); +} + +LONGBOW_TEST_CASE(parcURI, parcURI_SetScheme_Resetting) +{ + char *scheme = "scheme"; + PARCURI *uri = parcURI_Create(); + _parcURI_SetScheme(uri, scheme); + _parcURI_SetScheme(uri, scheme); + + assertTrue(strcmp(scheme, parcURI_GetScheme(uri)) == 0, + "Expected %s actual %s", scheme, parcURI_GetScheme(uri)); + parcURI_Release(&uri); +} + +LONGBOW_TEST_CASE(parcURI, parcURI_SetScheme_Reset) +{ + char *scheme = "scheme"; + PARCURI *uri = parcURI_Create(); + _parcURI_SetScheme(uri, scheme); + _parcURI_SetScheme(uri, NULL); + + assertNull(parcURI_GetScheme(uri), + "Expected NULL actual %s", parcURI_GetScheme(uri)); + parcURI_Release(&uri); +} + +LONGBOW_TEST_CASE(parcURI, parcURI_SetFragment) +{ + char *fragment = "fragment"; + PARCURI *uri = parcURI_Create(); + _parcURI_SetFragment(uri, fragment); + + assertTrue(strcmp(fragment, parcURI_GetFragment(uri)) == 0, + "Expected %s actual %s", fragment, parcURI_GetFragment(uri)); + parcURI_Release(&uri); +} + +LONGBOW_TEST_CASE(parcURI, parcURI_SetFragment_Resetting) +{ + char *fragment = "fragment"; + PARCURI *uri = parcURI_Create(); + _parcURI_SetFragment(uri, fragment); + _parcURI_SetFragment(uri, fragment); + + assertTrue(strcmp(fragment, parcURI_GetFragment(uri)) == 0, + "Expected %s actual %s", fragment, parcURI_GetFragment(uri)); + parcURI_Release(&uri); +} + +LONGBOW_TEST_CASE(parcURI, parcURI_SetFragment_Reset) +{ + char *fragment = "query"; + PARCURI *uri = parcURI_Create(); + _parcURI_SetFragment(uri, fragment); + _parcURI_SetFragment(uri, NULL); + + assertNull(parcURI_GetFragment(uri), + "Expected NULL actual %s", parcURI_GetFragment(uri)); + parcURI_Release(&uri); +} + +LONGBOW_TEST_CASE(parcURI, parcURI_SetQuery) +{ + char *query = "query"; + PARCURI *uri = parcURI_Create(); + _parcURI_SetQuery(uri, query); + + assertTrue(strcmp(query, parcURI_GetQuery(uri)) == 0, + "Expected %s actual %s", query, parcURI_GetQuery(uri)); + parcURI_Release(&uri); +} + +LONGBOW_TEST_CASE(parcURI, parcURI_SetQuery_Resetting) +{ + char *query = "query"; + PARCURI *uri = parcURI_Create(); + _parcURI_SetQuery(uri, query); + _parcURI_SetQuery(uri, query); + + assertTrue(strcmp(query, parcURI_GetQuery(uri)) == 0, + "Expected %s actual %s", query, parcURI_GetQuery(uri)); + parcURI_Release(&uri); +} + +LONGBOW_TEST_CASE(parcURI, parcURI_SetQuery_Reset) +{ + char *query = "query"; + PARCURI *uri = parcURI_Create(); + _parcURI_SetQuery(uri, query); + _parcURI_SetQuery(uri, NULL); + + assertNull(parcURI_GetQuery(uri), + "Expected NULL actual %s", parcURI_GetQuery(uri)); + parcURI_Release(&uri); +} + +LONGBOW_TEST_CASE(parcURI, parcURI_SetAuthority) +{ + char *authority = "authority@auth"; + PARCURI *uri = parcURI_Create(); + _parcURI_SetAuthority(uri, authority); + + assertTrue(strcmp(authority, parcURI_GetAuthority(uri)) == 0, + "Expected %s actual %s", authority, parcURI_GetAuthority(uri)); + parcURI_Release(&uri); +} + +LONGBOW_TEST_CASE(parcURI, parcURI_SetAuthority_Resetting) +{ + char *authority = "query"; + PARCURI *uri = parcURI_Create(); + _parcURI_SetAuthority(uri, authority); + _parcURI_SetAuthority(uri, authority); + + assertTrue(strcmp(authority, parcURI_GetAuthority(uri)) == 0, + "Expected %s actual %s", authority, parcURI_GetAuthority(uri)); + parcURI_Release(&uri); +} + +LONGBOW_TEST_CASE(parcURI, parcURI_SetAuthority_Reset) +{ + char *query = "query"; + PARCURI *uri = parcURI_Create(); + _parcURI_SetAuthority(uri, query); + _parcURI_SetAuthority(uri, NULL); + + assertNull(parcURI_GetAuthority(uri), + "Expected NULL actual %s", parcURI_GetAuthority(uri)); + parcURI_Release(&uri); +} + +LONGBOW_TEST_CASE(parcURI, parseAuthority_NoPath) +{ + char *authority = "//" URI_AUTHORITY; + char *expected = URI_AUTHORITY; + + const char *pointer; + char *actual = _parseAuthority(authority, &pointer); + + assertTrue(strcmp(expected, actual) == 0, + "Expected '%s' actual '%s'", authority, actual); + assertTrue(*pointer == 0, + "Expected null actual '%c'", *pointer); + parcMemory_Deallocate((void **) &actual); +} + +LONGBOW_TEST_CASE(parcURI, parseFragment) +{ + testUnimplemented("This test is unimplemented"); +} + +LONGBOW_TEST_CASE(parcURI, parsePath) +{ + testUnimplemented("This test is unimplemented"); +} + +LONGBOW_TEST_CASE(parcURI, parseQuery) +{ + testUnimplemented("This test is unimplemented"); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parcURI); + int status = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(status); +} diff --git a/libparc/parc/algol/test/test_parc_URIAuthority.c b/libparc/parc/algol/test/test_parc_URIAuthority.c new file mode 100644 index 00000000..e6d8196a --- /dev/null +++ b/libparc/parc/algol/test/test_parc_URIAuthority.c @@ -0,0 +1,267 @@ +/* + * 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 "../parc_URIAuthority.c" +#include <stdint.h> + +#include <LongBow/unit-test.h> + +#include <parc/algol/parc_URI.h> + +#include "_test_parc_URI.h" + +#include <parc/algol/parc_SafeMemory.h> +#include <parc/testing/parc_ObjectTesting.h> + +LONGBOW_TEST_RUNNER(parcURIAuthority) +{ + LONGBOW_RUN_TEST_FIXTURE(parcURIAuthority); +} + +LONGBOW_TEST_RUNNER_SETUP(parcURIAuthority) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_RUNNER_TEARDOWN(parcURIAuthority) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("Tests leak memory by %d allocations\n", outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(parcURIAuthority) +{ + LONGBOW_RUN_TEST_CASE(parcURIAuthority, parcURIAuthority_Parse); + + LONGBOW_RUN_TEST_CASE(parcURIAuthority, parcURIAuthority_Acquire); + + LONGBOW_RUN_TEST_CASE(parcURIAuthority, parcURIAuthority_Equals); + + LONGBOW_RUN_TEST_CASE(parcURIAuthority, parcURIAuthority_GetUserInfo); + LONGBOW_RUN_TEST_CASE(parcURIAuthority, parcURIAuthority_GetHostName); + LONGBOW_RUN_TEST_CASE(parcURIAuthority, parcURIAuthority_GetPort); +} + +LONGBOW_TEST_FIXTURE_SETUP(parcURIAuthority) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(parcURIAuthority) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(parcURIAuthority, parcURIAuthority_Parse) +{ + char *uriString = URI_SCHEME "://" URI_AUTHORITY "/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?" URI_QUERY "#" URI_FRAGMENT; + + uriString = parcMemory_StringDuplicate(uriString, strlen(uriString)); + + PARCURI *uri = parcURI_Parse(uriString); + + PARCURIAuthority *authority = parcURIAuthority_Parse(parcURI_GetAuthority(uri)); + + assertEqualStrings(parcURIAuthority_GetUserInfo(authority), URI_AUTHORITY_USERINFO); + + parcURIAuthority_Release(&authority); + + parcMemory_Deallocate((void **) &uriString); + parcURI_Release(&uri); + + // URI without the port + uriString = URI_SCHEME "://" URI_AUTHORITY_USERINFO "@" URI_AUTHORITY_HOSTNAME "/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?" URI_QUERY "#" URI_FRAGMENT; + uriString = parcMemory_StringDuplicate(uriString, strlen(uriString)); + uri = parcURI_Parse(uriString); + authority = parcURIAuthority_Parse(parcURI_GetAuthority(uri)); + + assertEqualStrings(parcURIAuthority_GetUserInfo(authority), URI_AUTHORITY_USERINFO); + + parcURIAuthority_Release(&authority); + parcURI_Release(&uri); + + // URI with literal V4 address + uriString = URI_SCHEME "://" URI_AUTHORITY_LITERAL_HOST "/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?" URI_QUERY "#" URI_FRAGMENT; + uriString = parcMemory_StringDuplicate(uriString, strlen(uriString)); + uri = parcURI_Parse(uriString); + authority = parcURIAuthority_Parse(parcURI_GetAuthority(uri)); + + assertEqualStrings(parcURIAuthority_GetHostName(authority), URI_AUTHORITY_LITERAL_HOSTNAME); + + parcURIAuthority_Release(&authority); + parcURI_Release(&uri); + + // URI with literal V6 address + uriString = URI_SCHEME "://" URI_AUTHORITY_LITERAL_HOST6 "/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?" URI_QUERY "#" URI_FRAGMENT; + uriString = parcMemory_StringDuplicate(uriString, strlen(uriString)); + uri = parcURI_Parse(uriString); + authority = parcURIAuthority_Parse(parcURI_GetAuthority(uri)); + + assertEqualStrings(parcURIAuthority_GetHostName(authority), URI_AUTHORITY_LITERAL_HOSTNAME6); + + parcURIAuthority_Release(&authority); + parcURI_Release(&uri); + + // URI with full literal V6 address + uriString = URI_SCHEME "://" URI_AUTHORITY_LITERAL_HOST6_2 "/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?" URI_QUERY "#" URI_FRAGMENT; + uriString = parcMemory_StringDuplicate(uriString, strlen(uriString)); + uri = parcURI_Parse(uriString); + authority = parcURIAuthority_Parse(parcURI_GetAuthority(uri)); + + assertEqualStrings(parcURIAuthority_GetHostName(authority), URI_AUTHORITY_LITERAL_HOSTNAME6_2); + + parcURIAuthority_Release(&authority); + parcURI_Release(&uri); + + parcMemory_Deallocate((void **) &uriString); +} + +LONGBOW_TEST_CASE(parcURIAuthority, parcURIAuthority_Acquire) +{ + char *uriString = URI_SCHEME "://" URI_AUTHORITY "/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?" URI_QUERY "#" URI_FRAGMENT; + + uriString = parcMemory_StringDuplicate(uriString, strlen(uriString)); + + PARCURI *uri = parcURI_Parse(uriString); + + PARCURIAuthority *authority = parcURIAuthority_Parse(parcURI_GetAuthority(uri)); + PARCURIAuthority *handle = parcURIAuthority_Acquire(authority); + + assertTrue(parcURIAuthority_Equals(authority, handle), "URI Authorities should be equal since they refer to the same object."); + + parcURIAuthority_Release(&authority); + parcURIAuthority_Release(&handle); + + parcMemory_Deallocate((void **) &uriString); + parcURI_Release(&uri); +} + +LONGBOW_TEST_CASE(parcURIAuthority, parcURIAuthority_Equals) +{ + char *uriString1 = URI_SCHEME "://" URI_AUTHORITY; + uriString1 = parcMemory_StringDuplicate(uriString1, strlen(uriString1)); + PARCURI *uri1 = parcURI_Parse(uriString1); + PARCURIAuthority *x = parcURIAuthority_Parse(parcURI_GetAuthority(uri1)); + + char *uriString2 = URI_SCHEME "://" URI_AUTHORITY; + uriString2 = parcMemory_StringDuplicate(uriString2, strlen(uriString2)); + PARCURI *uri2 = parcURI_Parse(uriString2); + PARCURIAuthority *y = parcURIAuthority_Parse(parcURI_GetAuthority(uri2)); + + char *uriString3 = URI_SCHEME "://" URI_AUTHORITY; + uriString3 = parcMemory_StringDuplicate(uriString3, strlen(uriString3)); + PARCURI *uri3 = parcURI_Parse(uriString3); + PARCURIAuthority *z = parcURIAuthority_Parse(parcURI_GetAuthority(uri3)); + + char *differentUriString = URI_SCHEME "://" URI_AUTHORITY_USERINFO; + differentUriString = parcMemory_StringDuplicate(differentUriString, strlen(differentUriString)); + PARCURI *unequalUri = parcURI_Parse(differentUriString); + PARCURIAuthority *u = parcURIAuthority_Parse(parcURI_GetAuthority(unequalUri)); + + char *uriString5 = URI_SCHEME "://" URI_AUTHORITY_DIFFERENT_PORT; + uriString5 = parcMemory_StringDuplicate(uriString5, strlen(uriString5)); + PARCURI *unequalUri5 = parcURI_Parse(uriString5); + PARCURIAuthority *u5 = parcURIAuthority_Parse(parcURI_GetAuthority(unequalUri5)); + + char *uriString4 = URI_SCHEME "://" URI_AUTHORITY_DIFFERENT_USER; + uriString4 = parcMemory_StringDuplicate(uriString4, strlen(uriString4)); + PARCURI *unequalUri4 = parcURI_Parse(uriString4); + PARCURIAuthority *u4 = parcURIAuthority_Parse(parcURI_GetAuthority(unequalUri4)); + + parcObjectTesting_AssertEqualsFunction(parcURIAuthority_Equals, x, y, z, u); + + assertFalse(parcURIAuthority_Equals(x, u4), "Expected URI authorities with different user info to be unequal"); + assertFalse(parcURIAuthority_Equals(x, u5), "Expected URI authorities with different hsot names to be unequal"); + + parcURIAuthority_Release(&x); + parcURIAuthority_Release(&y); + parcURIAuthority_Release(&z); + parcURIAuthority_Release(&u); + parcURIAuthority_Release(&u4); + parcURIAuthority_Release(&u5); + + parcMemory_Deallocate((void **) &uriString1); + parcMemory_Deallocate((void **) &uriString2); + parcMemory_Deallocate((void **) &uriString3); + parcMemory_Deallocate((void **) &uriString4); + parcMemory_Deallocate((void **) &uriString5); + parcMemory_Deallocate((void **) &differentUriString); + + parcURI_Release(&uri1); + parcURI_Release(&uri2); + parcURI_Release(&uri3); + parcURI_Release(&unequalUri); + parcURI_Release(&unequalUri4); + parcURI_Release(&unequalUri5); +} + +LONGBOW_TEST_CASE(parcURIAuthority, parcURIAuthority_GetUserInfo) +{ + char *uriString = URI_SCHEME "://" URI_AUTHORITY "/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?" URI_QUERY "#" URI_FRAGMENT; + uriString = parcMemory_StringDuplicate(uriString, strlen(uriString)); + PARCURI *uri = parcURI_Parse(uriString); + PARCURIAuthority *authority = parcURIAuthority_Parse(parcURI_GetAuthority(uri)); + + assertTrue(strcmp(URI_AUTHORITY_USERINFO, parcURIAuthority_GetUserInfo(authority)) == 0, "URI Authority user info should be equal: %s - %s", URI_AUTHORITY_USERINFO, parcURIAuthority_GetUserInfo(authority)); + + parcURIAuthority_Release(&authority); + + parcMemory_Deallocate((void **) &uriString); + parcURI_Release(&uri); +} + +LONGBOW_TEST_CASE(parcURIAuthority, parcURIAuthority_GetHostName) +{ + char *uriString = URI_SCHEME "://" URI_AUTHORITY "/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?" URI_QUERY "#" URI_FRAGMENT; + uriString = parcMemory_StringDuplicate(uriString, strlen(uriString)); + PARCURI *uri = parcURI_Parse(uriString); + PARCURIAuthority *authority = parcURIAuthority_Parse(parcURI_GetAuthority(uri)); + + assertTrue(strcmp(URI_AUTHORITY_HOSTNAME, parcURIAuthority_GetHostName(authority)) == 0, "URI Authority host name should be equal: %s - %s", URI_AUTHORITY_HOSTNAME, parcURIAuthority_GetHostName(authority)); + + parcURIAuthority_Release(&authority); + + parcMemory_Deallocate((void **) &uriString); + parcURI_Release(&uri); +} + +LONGBOW_TEST_CASE(parcURIAuthority, parcURIAuthority_GetPort) +{ + char *uriString = URI_SCHEME "://" URI_AUTHORITY "/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?" URI_QUERY "#" URI_FRAGMENT; + uriString = parcMemory_StringDuplicate(uriString, strlen(uriString)); + PARCURI *uri = parcURI_Parse(uriString); + PARCURIAuthority *authority = parcURIAuthority_Parse(parcURI_GetAuthority(uri)); + + assertTrue(atol(URI_AUTHORITY_PORT_1) == parcURIAuthority_GetPort(authority), + "URI Authority host name should be equal: %ld - %ld", atol(URI_AUTHORITY_PORT_1), parcURIAuthority_GetPort(authority)); + + parcURIAuthority_Release(&authority); + + parcMemory_Deallocate((void **) &uriString); + parcURI_Release(&uri); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parcURIAuthority); + int status = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(status); +} diff --git a/libparc/parc/algol/test/test_parc_URIPath.c b/libparc/parc/algol/test/test_parc_URIPath.c new file mode 100644 index 00000000..29f34a96 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_URIPath.c @@ -0,0 +1,406 @@ +/* + * 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 "../parc_URIPath.c" +#include <stdint.h> + +#include <LongBow/unit-test.h> + +#include <parc/algol/parc_URI.h> + +#include "_test_parc_URI.h" + +#include <parc/algol/parc_SafeMemory.h> +#include <parc/testing/parc_ObjectTesting.h> + +LONGBOW_TEST_RUNNER(parcURIPath) +{ + LONGBOW_RUN_TEST_FIXTURE(parcURIPath); +} + +LONGBOW_TEST_RUNNER_SETUP(parcURIPath) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_RUNNER_TEARDOWN(parcURIPath) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("Tests leak memory by %d allocations\n", outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(parcURIPath) +{ + LONGBOW_RUN_TEST_CASE(parcURIPath, parcURIPath_Acquire); + LONGBOW_RUN_TEST_CASE(parcURIPath, parcURIPath_Parse); + LONGBOW_RUN_TEST_CASE(parcURIPath, parcURIPath_Parse_WithQuery); + LONGBOW_RUN_TEST_CASE(parcURIPath, parcURIPath_Release); + LONGBOW_RUN_TEST_CASE(parcURIPath, parcURIPath_Count); + LONGBOW_RUN_TEST_CASE(parcURIPath, parcURIPath_Append); + LONGBOW_RUN_TEST_CASE(parcURIPath, parcURIPath_ToString); + LONGBOW_RUN_TEST_CASE(parcURIPath, parcURIPath_Length); + LONGBOW_RUN_TEST_CASE(parcURIPath, parcURIPath_Trim); + LONGBOW_RUN_TEST_CASE(parcURIPath, parcURIPath_Copy); + LONGBOW_RUN_TEST_CASE(parcURIPath, parcURIPath_Equals); + LONGBOW_RUN_TEST_CASE(parcURIPath, parcURIPath_Compare_Identity); + LONGBOW_RUN_TEST_CASE(parcURIPath, parcURIPath_Compare_Equal); + LONGBOW_RUN_TEST_CASE(parcURIPath, parcURIPath_Compare_Unequal); + LONGBOW_RUN_TEST_CASE(parcURIPath, parcURIPath_Compose); + LONGBOW_RUN_TEST_CASE(parcURIPath, parcURIPath_StartsWith); + LONGBOW_RUN_TEST_CASE(parcURIPath, parcURIPath_StartsWith_Equal); + LONGBOW_RUN_TEST_CASE(parcURIPath, parcURIPath_StartsWith_Fail); + LONGBOW_RUN_TEST_CASE(parcURIPath, parcURIPath_BuildString); +} + +LONGBOW_TEST_FIXTURE_SETUP(parcURIPath) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(parcURIPath) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(parcURIPath, parcURIPath_Acquire) +{ + const char *pointer; + PARCURIPath *path = parcURIPath_Parse("/" URI_PATH_SEGMENT "//////" URI_PATH_SEGMENT, &pointer); + PARCURIPath *handle = parcURIPath_Acquire(path); + + assertTrue(parcURIPath_Equals(path, handle), "URI paths should be equal: %s - %s", parcURIPath_ToString(path), parcURIPath_ToString(handle)); + + parcURIPath_Release(&path); + parcURIPath_Release(&handle); +} + +LONGBOW_TEST_CASE(parcURIPath, parcURIPath_Parse) +{ + const char *pointer; + PARCURIPath *path = parcURIPath_Parse("/" URI_PATH_SEGMENT "//////" URI_PATH_SEGMENT, &pointer); + assertNotNull(path, "Expected non-null result."); + assertTrue(*pointer == 0, "Expected pointer to point to the null terminating byte."); + + char *actualPath = parcURIPath_ToString(path); + + char *expectedPath = URI_PATH_SEGMENT "/" URI_PATH_SEGMENT; + assertTrue(strcmp(expectedPath, actualPath) == 0, "Expected %s actual %s", expectedPath, actualPath); + + parcMemory_Deallocate((void **) &actualPath); + parcURIPath_Release(&path); +} + +LONGBOW_TEST_CASE(parcURIPath, parcURIPath_Release) +{ + const char *pointer; + PARCURIPath *path = parcURIPath_Parse("/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT, &pointer); + assertNotNull(path, "Expected non-null result."); + parcURIPath_Release(&path); + assertNull(path, "Expected destroy to null the pointer."); +} + +LONGBOW_TEST_CASE(parcURIPath, parcURIPath_Parse_WithQuery) +{ + const char *pointer; + PARCURIPath *path = parcURIPath_Parse("/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?query", &pointer); + assertNotNull(path, "Expected non-null result."); + assertTrue(*pointer == '?', "Expected pointer to point to the terminating character."); + + char *actualPath = parcURIPath_ToString(path); + char *expectedPath = URI_PATH_SEGMENT "/" URI_PATH_SEGMENT; + assertTrue(strcmp(expectedPath, actualPath) == 0, "Expected %s actual %s", expectedPath, actualPath); + parcMemory_Deallocate((void **) &actualPath); + parcURIPath_Release(&path); +} + +LONGBOW_TEST_CASE(parcURIPath, parcURIPath_Equals) +{ + const char *pointer; + PARCURIPath *x = parcURIPath_Parse("/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?query", &pointer); + PARCURIPath *y = parcURIPath_Parse("/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?query", &pointer); + PARCURIPath *z = parcURIPath_Parse("/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?query", &pointer); + PARCURIPath *u1 = parcURIPath_Parse("/" URI_PATH_SEGMENT "a/" URI_PATH_SEGMENT "?query", &pointer); + PARCURIPath *u2 = parcURIPath_Parse("/" URI_PATH_SEGMENT "?query", &pointer); + + parcObjectTesting_AssertEqualsFunction(parcURIPath_Equals, x, y, z, u1, u2); + + parcURIPath_Release(&x); + parcURIPath_Release(&y); + parcURIPath_Release(&z); + parcURIPath_Release(&u1); + parcURIPath_Release(&u2); +} + +LONGBOW_TEST_CASE(parcURIPath, parcURIPath_Copy) +{ + const char *pointer; + PARCURIPath *path = parcURIPath_Parse("/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?query", &pointer); + PARCURIPath *copy = parcURIPath_Copy(path); + + assertTrue(copy != path, "Expected distinct instances of the path."); + + int comparison = parcURIPath_Compare(path, copy); + + assertTrue(comparison == 0, "Expected the copy to compare equal to the original."); + + parcURIPath_Release(&path); + parcURIPath_Release(©); +} + +LONGBOW_TEST_CASE(parcURIPath, parcURIPath_StartsWith) +{ + const char *pointer; + PARCURIPath *base = parcURIPath_Parse("/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?query", &pointer); + PARCURIPath *prefix = parcURIPath_Parse("/" URI_PATH_SEGMENT "?query", &pointer); + + bool actual = parcURIPath_StartsWith(base, prefix); + + assertTrue(actual, "Expected true, actual false."); + + parcURIPath_Release(&prefix); + parcURIPath_Release(&base); +} + +LONGBOW_TEST_CASE(parcURIPath, parcURIPath_StartsWith_Equal) +{ + const char *pointer; + PARCURIPath *base = parcURIPath_Parse("/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?query", &pointer); + PARCURIPath *prefix = parcURIPath_Parse("/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?query", &pointer); + + bool actual = parcURIPath_StartsWith(base, prefix); + + assertTrue(actual, "Expected true, actual false."); + + parcURIPath_Release(&prefix); + parcURIPath_Release(&base); +} + +LONGBOW_TEST_CASE(parcURIPath, parcURIPath_StartsWith_Fail) +{ + const char *pointer; + PARCURIPath *base = parcURIPath_Parse("/" URI_PATH_SEGMENT "?query", &pointer); + PARCURIPath *prefix1 = parcURIPath_Parse("/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?query", &pointer); + PARCURIPath *prefix2 = parcURIPath_Parse("/" URI_PATH_SEGMENT "A?query", &pointer); + + assertFalse(parcURIPath_StartsWith(base, prefix1), + "Expected false, actual true"); + + assertFalse(parcURIPath_StartsWith(base, prefix2), + "Expected false, actual true"); + + parcURIPath_Release(&prefix1); + parcURIPath_Release(&prefix2); + parcURIPath_Release(&base); +} + +LONGBOW_TEST_CASE(parcURIPath, parcURIPath_Compose) +{ + const char *pointer; + PARCURIPath *base = parcURIPath_Parse("/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?query", &pointer); + PARCURIPath *expected = parcURIPath_Parse("/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?query", &pointer); + + PARCURISegment *a = parcURISegment_Parse(URI_PATH_SEGMENT, &pointer); + PARCURIPath *actual = parcURIPath_Compose(base, a, a, NULL); + parcURISegment_Release(&a); + + char *actualString = parcURIPath_ToString(actual); + char *expectedString = parcURIPath_ToString(expected); + assertTrue(parcURIPath_Compare(expected, actual) == 0, "Expected '%s' actual '%s'\n", expectedString, actualString); + + parcMemory_Deallocate((void **) &actualString); + parcMemory_Deallocate((void **) &expectedString); + + parcURIPath_Release(&actual); + + parcURIPath_Release(&expected); + parcURIPath_Release(&base); +} + +LONGBOW_TEST_CASE(parcURIPath, parcURIPath_Compare_Identity) +{ + const char *pointer; + PARCURIPath *path = parcURIPath_Parse("/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?query", &pointer); + + PARCURIPath *equivalents[] = { + path, + parcURIPath_Parse("/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?query",&pointer), + NULL, + }; + PARCURIPath *lessers[] = { + parcURIPath_Parse("/" URI_PATH_SEGMENT "?query", &pointer), + NULL, + }; + PARCURIPath *greaters[] = { + parcURIPath_Parse("/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?query", &pointer), + parcURIPath_Parse("/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "AAA?query", &pointer), + NULL, + }; + + parcObjectTesting_AssertCompareTo(parcURIPath_Compare, path, equivalents, lessers, greaters); + + for (int i = 0; equivalents[i] != NULL; i++) { + parcURIPath_Release(&equivalents[i]); + } + for (int i = 0; lessers[i] != NULL; i++) { + parcURIPath_Release(&lessers[i]); + } + for (int i = 0; greaters[i] != NULL; i++) { + parcURIPath_Release(&greaters[i]); + } +} + +LONGBOW_TEST_CASE(parcURIPath, parcURIPath_Compare_Equal) +{ + const char *pointer; + PARCURIPath *pathA = parcURIPath_Parse("/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?query", &pointer); + PARCURIPath *pathB = parcURIPath_Parse("/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?query", &pointer); + + int comparison = parcURIPath_Compare(pathA, pathB); + + assertTrue(comparison == 0, "Expected 0: equal paths to compare equal. Actual %d", comparison); + parcURIPath_Release(&pathA); + parcURIPath_Release(&pathB); +} + +LONGBOW_TEST_CASE(parcURIPath, parcURIPath_Compare_Unequal) +{ + const char *pointer; + PARCURIPath *pathA = parcURIPath_Parse("/" URI_PATH_SEGMENT "?query", &pointer); + PARCURIPath *pathB = parcURIPath_Parse("/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "?query", &pointer); + + int comparison = parcURIPath_Compare(pathA, pathB); + + assertTrue(comparison < 0, "Expected < 0: path A is less than path B. Actual %d", comparison); + parcURIPath_Release(&pathA); + parcURIPath_Release(&pathB); +} + +LONGBOW_TEST_CASE(parcURIPath, parcURIPath_Count) +{ + const char *pointer; + PARCURIPath *path = parcURIPath_Parse("/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT, &pointer); + assertNotNull(path, "Expected non-null result."); + assertTrue(*pointer == 0, "Expected pointer to point to the null terminating byte."); + + size_t actual = parcURIPath_Count(path); + assertTrue(3 == actual, "Expected %d actual %zd", 3, actual); + + parcURIPath_Release(&path); +} + +LONGBOW_TEST_CASE(parcURIPath, parcURIPath_ToString) +{ + const char *pointer; + PARCURIPath *path = parcURIPath_Parse("/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT, &pointer); + assertNotNull(path, "Expected non-null result."); + assertTrue(*pointer == 0, "Expected pointer to point to the null terminating byte."); + + char *actualString = parcURIPath_ToString(path); + + char *expectedString = URI_PATH_SEGMENT "/" URI_PATH_SEGMENT; + assertTrue(strcmp(expectedString, actualString) == 0, "Expected %s actual %s", expectedString, actualString); + + parcMemory_Deallocate((void **) &actualString); + parcURIPath_Release(&path); +} + +LONGBOW_TEST_CASE(parcURIPath, parcURIPath_Length) +{ + const char *pointer; + PARCURIPath *path = parcURIPath_Parse("/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT, &pointer); + assertNotNull(path, "Expected non-null result."); + assertTrue(*pointer == 0, "Expected pointer to point to the null terminating byte."); + + size_t actualCount = parcURIPath_Length(path); + + size_t expectedCount = 79; + assertTrue(expectedCount == actualCount, "Expected %zd actual %zd", expectedCount, actualCount); + + parcURIPath_Release(&path); +} + +LONGBOW_TEST_CASE(parcURIPath, parcURIPath_Append) +{ + const char *pointer; + PARCURIPath *path = parcURIPath_Parse("/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT, &pointer); + + PARCURISegment *segment = parcURISegment_Parse(URI_PATH_SEGMENT, &pointer); + + PARCURIPath *result = parcURIPath_Append(path, segment); + assertTrue(result == path, "Expected %p, actual %p", (void *) path, (void *) result); + + size_t actualCount = parcURIPath_Count(path); + assertTrue(3 == actualCount, "Expected 3, actual %zd", actualCount); + + char *actualPath = parcURIPath_ToString(path); + char *expectedPath = URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT; + assertTrue(strcmp(expectedPath, actualPath) == 0, "Expected %s actual %s", expectedPath, actualPath); + + parcMemory_Deallocate((void **) &actualPath); + parcURIPath_Release(&path); +} + +LONGBOW_TEST_CASE(parcURIPath, parcURIPath_Trim) +{ + const char *pointer; + PARCURIPath *path = parcURIPath_Parse("/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT "/" URI_PATH_SEGMENT, &pointer); + + parcURIPath_Trim(path, 2); + size_t actualCount = parcURIPath_Count(path); + assertTrue(2 == actualCount, "Expected 2, actual %zd", actualCount); + + parcURIPath_Release(&path); +} + +LONGBOW_TEST_CASE(parcURIPath, parcURIPath_BuildString) +{ + const char *pointer; + PARCURIPath *path = parcURIPath_Parse("/" URI_PATH_SEGMENT, &pointer); + PARCBufferComposer *target = parcBufferComposer_Create(); + + PARCBufferComposer *string = parcBufferComposer_Create(); + parcBufferComposer_PutString(string, URI_PATH_SEGMENT); + + PARCBuffer *b1 = parcBufferComposer_ProduceBuffer(string); + char *string1 = parcBuffer_ToString(b1); + parcBuffer_Release(&b1); + + parcURIPath_BuildString(path, target); + PARCBuffer *b2 = parcBufferComposer_ProduceBuffer(target); + char *string2 = parcBuffer_ToString(b2); + parcBuffer_Release(&b2); + + assertTrue(strncmp(string1, string2, strlen(string1)) == 0, "String representations of the paths should be equal: %s - %s", string1, string2); + + parcMemory_Deallocate((void **) &string1); + parcMemory_Deallocate((void **) &string2); + + parcBufferComposer_Release(&string); + parcBufferComposer_Release(&target); + parcURIPath_Release(&path); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parcURIPath); + int status = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(status); +} diff --git a/libparc/parc/algol/test/test_parc_URISegment.c b/libparc/parc/algol/test/test_parc_URISegment.c new file mode 100644 index 00000000..640bab89 --- /dev/null +++ b/libparc/parc/algol/test/test_parc_URISegment.c @@ -0,0 +1,315 @@ +/* + * 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 "../parc_URISegment.c" +#include <LongBow/unit-test.h> + +#include <stdint.h> + +#include <parc/algol/parc_URI.h> + +#include "_test_parc_URI.h" + +#include <parc/algol/parc_SafeMemory.h> +#include <parc/testing/parc_ObjectTesting.h> + +LONGBOW_TEST_RUNNER(parcURISegment) +{ + LONGBOW_RUN_TEST_FIXTURE(parcURISegment); +} + +LONGBOW_TEST_RUNNER_SETUP(parcURISegment) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_RUNNER_TEARDOWN(parcURISegment) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("Tests leak memory by %d allocations\n", outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(parcURISegment) +{ + LONGBOW_RUN_TEST_CASE(parcURISegment, _parcURISegment_fromHexDigit); + LONGBOW_RUN_TEST_CASE(parcURISegment, _parcURISegment_parsePercentEncoded); + + LONGBOW_RUN_TEST_CASE(parcURISegment, parcURISegment_Acquire); + LONGBOW_RUN_TEST_CASE(parcURISegment, parcURISegment_Create); + LONGBOW_RUN_TEST_CASE(parcURISegment, parcURISegment_Parse); + LONGBOW_RUN_TEST_CASE(parcURISegment, parcURISegment_Parse_WithExtraSlashes); + LONGBOW_RUN_TEST_CASE(parcURISegment, parcURISegment_Parse_WithInvalidPercentage); + LONGBOW_RUN_TEST_CASE(parcURISegment, parcURISegment_Release); + LONGBOW_RUN_TEST_CASE(parcURISegment, parcURISegment_Length); + LONGBOW_RUN_TEST_CASE(parcURISegment, parcURISegment_ToString); + LONGBOW_RUN_TEST_CASE(parcURISegment, parcURISegment_Equals_Contract); + LONGBOW_RUN_TEST_CASE(parcURISegment, parcURISegment_Compare_Contract); + LONGBOW_RUN_TEST_CASE(parcURISegment, parcURISegment_Clone); + LONGBOW_RUN_TEST_CASE(parcURISegment, parcURISegment_GetBuffer); +} + +LONGBOW_TEST_FIXTURE_SETUP(parcURISegment) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(parcURISegment) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(parcURISegment, _parcURISegment_fromHexDigit) +{ + const char test = 'G'; + signed char actual = _fromHexDigit(test); + assertTrue(-1 == (int) actual, "Invalid hex digit should not be changed to a decimal value, we expect -1 as the result"); +} + +LONGBOW_TEST_CASE(parcURISegment, _parcURISegment_parsePercentEncoded) +{ + unsigned char buffer; + + const char *test1 = "0G"; + const char *result1 = _parsePercentEncoded(test1, &buffer); + assertTrue(NULL == result1, "Expected NULL parsed byte from invalid encoded percentage string, got %s", result1); + const char *test2 = "GG"; + const char *result2 = _parsePercentEncoded(test2, &buffer); + assertTrue(NULL == result2, "Expected NULL parsed byte from invalid encoded percentage string, got %s", result2); + const char *test3 = ""; + const char *result3 = _parsePercentEncoded(test3, &buffer); + assertTrue(NULL == result3, "Expected NULL parsed byte from empty encoded percentage string, got %s", result3); + const char *test4 = "0"; + const char *result4 = _parsePercentEncoded(test4, &buffer); + assertTrue(NULL == result4, "Expected NULL parsed byte from half-empty encoded percentage string, got %s", result4); +} + +LONGBOW_TEST_CASE(parcURISegment, parcURISegment_Acquire) +{ + char *expected = URI_PATH_SEGMENT; + + const char *pointer; + PARCURISegment *segment = parcURISegment_Parse(expected, &pointer); + PARCURISegment *handle = parcURISegment_Acquire(segment); + assertTrue(parcURISegment_Equals(segment, handle), "Expected URI segments to be equal: %s - %s", parcURISegment_ToString(segment), parcURISegment_ToString(handle)); + + parcURISegment_Release(&segment); + parcURISegment_Release(&handle); +} + +LONGBOW_TEST_CASE(parcURISegment, parcURISegment_Create) +{ + char *expected = URI_PATH_SEGMENT; + + PARCURISegment *segment = parcURISegment_Create(strlen(expected), (unsigned char *) expected); + assertNotNull(segment, "Expected non-null result."); + + parcURISegment_Release(&segment); +} + +LONGBOW_TEST_CASE(parcURISegment, parcURISegment_Parse) +{ + char *expected = URI_PATH_SEGMENT; + + const char *pointer; + PARCURISegment *segment = parcURISegment_Parse(expected, &pointer); + assertNotNull(segment, "Expected non-null result."); + + char *expectedBytes = URI_PATH_SEGMENT; + + char *actualBytes = parcURISegment_ToString(segment); + + assertTrue(strcmp(expectedBytes, actualBytes) == 0, + "Expected %s actual %s", expectedBytes, actualBytes); + parcMemory_Deallocate((void **) &actualBytes); + + assertTrue(parcURISegment_Length(segment) == 39, + "Expected 39, actual %zd", parcURISegment_Length(segment)); + assertTrue(*pointer == 0, "Expected pointer to point to the null terminating byte."); + + parcURISegment_Release(&segment); +} + +LONGBOW_TEST_CASE(parcURISegment, parcURISegment_Parse_WithExtraSlashes) +{ + const char *pointer; + PARCURISegment *segment = parcURISegment_Parse(URI_PATH_SEGMENT_WITH_SLASHES, &pointer); + assertNotNull(segment, "Expected non-null result."); + + char *expectedBytes = URI_PATH_SEGMENT; + + char *actualBytes = parcURISegment_ToString(segment); + + assertTrue(strcmp(expectedBytes, actualBytes) == 0, + "Expected %s actual %s", expectedBytes, actualBytes); + parcMemory_Deallocate((void **) &actualBytes); + + assertTrue(parcURISegment_Length(segment) == 39, + "Expected 39, actual %zd", parcURISegment_Length(segment)); + assertTrue(*pointer == '/', "Expected pointer to point to the slash character: %c", *pointer); + + parcURISegment_Release(&segment); +} + +LONGBOW_TEST_CASE(parcURISegment, parcURISegment_Parse_WithInvalidPercentage) +{ + const char *pointer; + PARCURISegment *segment = parcURISegment_Parse(URI_PATH_SEGMENT "%G", &pointer); + + assertNull(segment, "Parsed segment should be NULL since the last percent-encoded byte is invalid"); +} + +LONGBOW_TEST_CASE(parcURISegment, parcURISegment_Release) +{ + char *expected = URI_PATH_SEGMENT; + + const char *pointer; + PARCURISegment *segment = parcURISegment_Parse(expected, &pointer); + assertNotNull(segment, "Expected non-null result."); + + parcURISegment_Release(&segment); + assertNull(segment, "Expected destroy to null the pointer"); +} + +LONGBOW_TEST_CASE(parcURISegment, parcURISegment_Equals_Contract) +{ + char *expected = URI_PATH_SEGMENT; + + const char *pointer; + PARCURISegment *x = parcURISegment_Parse(expected, &pointer); + PARCURISegment *y = parcURISegment_Parse(expected, &pointer); + PARCURISegment *z = parcURISegment_Parse(expected, &pointer); + + parcObjectTesting_AssertEqualsFunction(parcURISegment_Equals, x, y, z, NULL); + + parcURISegment_Release(&x); + parcURISegment_Release(&y); + parcURISegment_Release(&z); +} + +LONGBOW_TEST_CASE(parcURISegment, parcURISegment_Clone) +{ + char *expected = URI_PATH_SEGMENT; + + const char *pointer; + PARCURISegment *segment = parcURISegment_Parse(expected, &pointer); + PARCURISegment *copy = parcURISegment_Clone(segment); + + assertTrue(segment != copy, "Expected different instances of equal segments."); + + int comparison = parcURISegment_Compare(segment, copy); + assertTrue(comparison == 0, "Expected equal segments."); + + assertTrue(parcURISegment_Equals(segment, copy), "Expected equal segments"); + + parcURISegment_Release(©); + parcURISegment_Release(&segment); +} + +LONGBOW_TEST_CASE(parcURISegment, parcURISegment_Length) +{ + const char *pointer; + PARCURISegment *segment = parcURISegment_Parse(URI_PATH_SEGMENT, &pointer); + assertNotNull(segment, + "Expected non-null result."); + assertTrue(*pointer == 0, + "Expected pointer to point to the null terminating byte."); + + size_t actual = parcURISegment_Length(segment); + + assertTrue(actual == 39, + "Expected 39, actual %zd", actual); + + parcURISegment_Release(&segment); +} + +LONGBOW_TEST_CASE(parcURISegment, parcURISegment_Compare_Contract) +{ + const char *pointer; + PARCURISegment *segment = parcURISegment_Parse("MMM", &pointer); + + PARCURISegment *equivalents[] = { + segment, + parcURISegment_Parse("MMM",&pointer), + NULL, + }; + PARCURISegment *lessers[] = { + parcURISegment_Parse("MM", &pointer), + parcURISegment_Parse("MML", &pointer), + NULL, + }; + PARCURISegment *greaters[] = { + parcURISegment_Parse("MMMM", &pointer), + parcURISegment_Parse("MMN", &pointer), + NULL, + }; + parcObjectTesting_AssertCompareTo(parcURISegment_Compare, segment, equivalents, lessers, greaters); + + for (int i = 0; equivalents[i] != NULL; i++) { + parcURISegment_Release(&equivalents[i]); + } + for (int i = 0; lessers[i] != NULL; i++) { + parcURISegment_Release(&lessers[i]); + } + for (int i = 0; greaters[i] != NULL; i++) { + parcURISegment_Release(&greaters[i]); + } +} + +LONGBOW_TEST_CASE(parcURISegment, parcURISegment_ToString) +{ + const char *pointer; + PARCURISegment *segment = parcURISegment_Parse(URI_PATH_SEGMENT, &pointer); + assertNotNull(segment, "Expected non-null result."); + assertTrue(*pointer == 0, "Expected pointer to point to the null terminating byte."); + + char *actual = parcURISegment_ToString(segment); + + assertTrue(strcmp(URI_PATH_SEGMENT, actual) == 0, "Expected %s, actual %s", URI_PATH_SEGMENT, actual); + + parcURISegment_Release(&segment); + + parcMemory_Deallocate((void **) &actual); +} + +LONGBOW_TEST_CASE(parcURISegment, parcURISegment_GetBuffer) +{ + const char *pointer; + PARCURISegment *segment = parcURISegment_Parse(URI_PATH_SEGMENT, &pointer); + assertNotNull(segment, "Expected non-null result."); + assertTrue(*pointer == 0, "Expected pointer to point to the null terminating byte."); + + PARCBuffer *buffer = parcURISegment_GetBuffer(segment); + + char *expected = URI_PATH_SEGMENT; + char *actual = (char *) parcBuffer_Overlay(buffer, 0); + size_t compareLength = strlen(URI_PATH_SEGMENT); + assertTrue(strncmp(expected, actual, compareLength), "Buffer does not contain original data."); + + parcURISegment_Release(&segment); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parcURISegment); + int status = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(status); +} diff --git a/libparc/parc/algol/test/test_parc_Varint.c b/libparc/parc/algol/test/test_parc_Varint.c new file mode 100755 index 00000000..cf0c865c --- /dev/null +++ b/libparc/parc/algol/test/test_parc_Varint.c @@ -0,0 +1,310 @@ +/* + * 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 "../parc_Varint.c" + +#include <LongBow/unit-test.h> + +#include <parc/algol/parc_SafeMemory.h> +#include <parc/algol/parc_BufferComposer.h> + +LONGBOW_TEST_RUNNER(parc_VarInt) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +LONGBOW_TEST_RUNNER_SETUP(parc_VarInt) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_RUNNER_TEARDOWN(parc_VarInt) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcVarint_And); + LONGBOW_RUN_TEST_CASE(Global, parcVarint_AndUint16); + LONGBOW_RUN_TEST_CASE(Global, parcVarint_AndUint32); + LONGBOW_RUN_TEST_CASE(Global, parcVarint_AndUint64); + LONGBOW_RUN_TEST_CASE(Global, parcVarint_AndUint8); + LONGBOW_RUN_TEST_CASE(Global, parcVarint_AsSize); + LONGBOW_RUN_TEST_CASE(Global, parcVarint_AsUint16); + LONGBOW_RUN_TEST_CASE(Global, parcVarint_AsUint32); + LONGBOW_RUN_TEST_CASE(Global, parcVarint_AsUint64); + LONGBOW_RUN_TEST_CASE(Global, parcVarint_AsUint8); + LONGBOW_RUN_TEST_CASE(Global, parcVarint_Destroy); + LONGBOW_RUN_TEST_CASE(Global, parcVarint_Equals); + LONGBOW_RUN_TEST_CASE(Global, parcVarint_EqualsUint16); + LONGBOW_RUN_TEST_CASE(Global, parcVarint_EqualsUint32); + LONGBOW_RUN_TEST_CASE(Global, parcVarint_EqualsUint64); + LONGBOW_RUN_TEST_CASE(Global, parcVarint_EqualsUint8); + LONGBOW_RUN_TEST_CASE(Global, parcVarint_FromByteBuffer); + LONGBOW_RUN_TEST_CASE(Global, parcVarint_FromUTF8ByteBuffer); + LONGBOW_RUN_TEST_CASE(Global, parcVarint_FromUint32); + LONGBOW_RUN_TEST_CASE(Global, parcVarint_FromUint64); + LONGBOW_RUN_TEST_CASE(Global, parcVarint_FromUint8); + LONGBOW_RUN_TEST_CASE(Global, parcVarint_New); + LONGBOW_RUN_TEST_CASE(Global, parcVarint_Or); + LONGBOW_RUN_TEST_CASE(Global, parcVarint_OrUint16); + LONGBOW_RUN_TEST_CASE(Global, parcVarint_OrUint32); + LONGBOW_RUN_TEST_CASE(Global, parcVarint_OrUint64); + LONGBOW_RUN_TEST_CASE(Global, parcVarint_OrUint8); + LONGBOW_RUN_TEST_CASE(Global, parcVarint_Set); + LONGBOW_RUN_TEST_CASE(Global, parcVarint_ShiftLeft); + LONGBOW_RUN_TEST_CASE(Global, parcVarint_ShiftRight); + LONGBOW_RUN_TEST_CASE(Global, parcVarint_ToString); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcVarint_And) +{ + testUnimplemented("This test is unimplemented"); +} + +LONGBOW_TEST_CASE(Global, parcVarint_AndUint16) +{ + testUnimplemented("This test is unimplemented"); +} + +LONGBOW_TEST_CASE(Global, parcVarint_AndUint32) +{ + testUnimplemented("This test is unimplemented"); +} + +LONGBOW_TEST_CASE(Global, parcVarint_AndUint64) +{ + testUnimplemented("This test is unimplemented"); +} + +LONGBOW_TEST_CASE(Global, parcVarint_AndUint8) +{ + testUnimplemented("This test is unimplemented"); +} + +LONGBOW_TEST_CASE(Global, parcVarint_AsSize) +{ + testUnimplemented("This test is unimplemented"); +} + +LONGBOW_TEST_CASE(Global, parcVarint_AsUint16) +{ + testUnimplemented("This test is unimplemented"); +} + +LONGBOW_TEST_CASE(Global, parcVarint_AsUint32) +{ + testUnimplemented("This test is unimplemented"); +} + +LONGBOW_TEST_CASE(Global, parcVarint_AsUint64) +{ + testUnimplemented("This test is unimplemented"); +} + +LONGBOW_TEST_CASE(Global, parcVarint_AsUint8) +{ + testUnimplemented("This test is unimplemented"); +} + +LONGBOW_TEST_CASE(Global, parcVarint_Destroy) +{ + testUnimplemented("This test is unimplemented"); +} + +LONGBOW_TEST_CASE(Global, parcVarint_Equals) +{ + testUnimplemented("This test is unimplemented"); +} + +LONGBOW_TEST_CASE(Global, parcVarint_EqualsUint16) +{ + testUnimplemented("This test is unimplemented"); +} + +LONGBOW_TEST_CASE(Global, parcVarint_EqualsUint32) +{ + testUnimplemented("This test is unimplemented"); +} + +LONGBOW_TEST_CASE(Global, parcVarint_EqualsUint64) +{ + testUnimplemented("This test is unimplemented"); +} + +LONGBOW_TEST_CASE(Global, parcVarint_EqualsUint8) +{ + unsigned char expected = 5; + PARCVarint *a = parcVarint_FromUint8(expected); + PARCVarint *b = parcVarint_FromUint8(expected); + + assertTrue(parcVarint_Equals(a, b), "Equal instances failed to be equal."); + parcVarint_Destroy(&a); + parcVarint_Destroy(&b); +} + +LONGBOW_TEST_CASE(Global, parcVarint_FromByteBuffer) +{ + testUnimplemented("This test is unimplemented"); +} + +LONGBOW_TEST_CASE(Global, parcVarint_FromUTF8ByteBuffer) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + parcBufferComposer_PutString(composer, "10"); + PARCBuffer *buffer = parcBufferComposer_ProduceBuffer(composer); + + PARCVarint *varint = parcVarint_FromUTF8ByteBuffer(buffer); + + uint32_t actual = parcVarint_AsUint32(varint); + + assertTrue(10 == actual, "Expected 10 actual %u", actual); + parcBuffer_Release(&buffer); + parcBufferComposer_Release(&composer); + parcVarint_Destroy(&varint); +} + +LONGBOW_TEST_CASE(Global, parcVarint_FromUint32) +{ + uint32_t value = 0x12345678; + PARCVarint *a = parcVarint_FromUint32(value); + assertNotNull(a, "Probably out of memory."); + + uint32_t actual = parcVarint_AsUint32(a); + + assertEqual(value, actual, "%u"); + + parcVarint_Destroy(&a); +} + +LONGBOW_TEST_CASE(Global, parcVarint_FromUint64) +{ + uint64_t value = 0x1234567812345678; + PARCVarint *a = parcVarint_FromUint64(value); + assertNotNull(a, "Probably out of memory."); + + uint64_t actual = parcVarint_AsUint64(a); + + assertEqual(value, actual, "%" PRIu64); + + parcVarint_Destroy(&a); +} + +LONGBOW_TEST_CASE(Global, parcVarint_FromUint8) +{ + uint8_t value = 0x12; + PARCVarint *a = parcVarint_FromUint8(value); + assertNotNull(a, "Probably out of memory."); + + uint8_t actual = parcVarint_AsUint8(a); + + assertEqual(value, actual, "%d"); + + parcVarint_Destroy(&a); +} + +LONGBOW_TEST_CASE(Global, parcVarint_New) +{ + PARCVarint *a = parcVarint_Create(); + assertNotNull(a, "Probably out of memory."); + + parcVarint_Destroy(&a); + assertNull(a, "Destroy failed to nullify."); +} + +LONGBOW_TEST_CASE(Global, parcVarint_Or) +{ + testUnimplemented("This test is unimplemented"); +} + +LONGBOW_TEST_CASE(Global, parcVarint_OrUint16) +{ + testUnimplemented("This test is unimplemented"); +} + +LONGBOW_TEST_CASE(Global, parcVarint_OrUint32) +{ + testUnimplemented("This test is unimplemented"); +} + +LONGBOW_TEST_CASE(Global, parcVarint_OrUint64) +{ + testUnimplemented("This test is unimplemented"); +} + +LONGBOW_TEST_CASE(Global, parcVarint_OrUint8) +{ + testUnimplemented("This test is unimplemented"); +} + +LONGBOW_TEST_CASE(Global, parcVarint_Set) +{ + testUnimplemented("This test is unimplemented"); +} + +LONGBOW_TEST_CASE(Global, parcVarint_ShiftLeft) +{ + testUnimplemented("This test is unimplemented"); +} + +LONGBOW_TEST_CASE(Global, parcVarint_ShiftRight) +{ + testUnimplemented("This test is unimplemented"); +} + +LONGBOW_TEST_CASE(Global, parcVarint_ToString) +{ + testUnimplemented("This test is unimplemented"); +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_VarInt); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/algol/test/test_parc_Vector.c b/libparc/parc/algol/test/test_parc_Vector.c new file mode 100644 index 00000000..bea435dc --- /dev/null +++ b/libparc/parc/algol/test/test_parc_Vector.c @@ -0,0 +1,116 @@ +/* + * 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 <LongBow/unit-test.h> +#include <LongBow/debugging.h> + +#include <parc/algol/parc_SafeMemory.h> + +#include "../parc_Vector.c" + +LONGBOW_TEST_RUNNER(PARCVector) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +LONGBOW_TEST_RUNNER_SETUP(PARCVector) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_RUNNER_TEARDOWN(PARCVector) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("Tests leak memory by %d allocations\n", outstandingAllocations); + exit(1); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcVectorDestroy); + LONGBOW_RUN_TEST_CASE(Global, parcVectorGetLength); + LONGBOW_RUN_TEST_CASE(Global, parcVectorGetPointer); + LONGBOW_RUN_TEST_CASE(Global, parcVectorCreate); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcVectorDestroy) +{ + const char *string = "Hello World"; + + PARCVector *x = parcVector_Create((void *) string, strlen(string)); + parcVector_Destroy(&x); + assertNull(x, "Destroy did not nullify."); +} + +LONGBOW_TEST_CASE(Global, parcVectorGetLength) +{ + const char *string = "Hello World"; + + PARCVector *x = parcVector_Create((void *) string, strlen(string)); + size_t expected = strlen(string); + size_t actual = parcVector_GetLength(x); + assertEqual(expected, actual, "%zd"); + parcVector_Destroy(&x); +} + +LONGBOW_TEST_CASE(Global, parcVectorGetPointer) +{ + const char *expected = "Hello World"; + + PARCVector *x = parcVector_Create((void *) expected, strlen(expected)); + const char *actual = parcVector_GetPointer(x); + + assertEqual((void *) expected, (void *) actual, "%p"); + + parcVector_Destroy(&x); +} + +LONGBOW_TEST_CASE(Global, parcVectorCreate) +{ + const char *string = "Hello World"; + + PARCVector *x = parcVector_Create((void *) string, strlen(string)); + assertNotNull(x, "Probably out of memory."); + parcVector_Destroy(&x); +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(PARCVector); + int status = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(status); +} |