summaryrefslogtreecommitdiff
path: root/portable/src
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:30:40 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:30:40 -0800
commit4a68b3365c8c50aa93505e99ead2565ab73dcdb0 (patch)
treef2f3008ab6b78f4550a56893ad9f96e91c75f0fd /portable/src
parente6733b78b94eb6701345e1a7244ab54dad60d47d (diff)
downloadsrec-4a68b3365c8c50aa93505e99ead2565ab73dcdb0.tar.gz
Diffstat (limited to 'portable/src')
-rw-r--r--portable/src/ArrayList.c132
-rw-r--r--portable/src/ArrayListImpl.c241
-rw-r--r--portable/src/ESR_ReturnCode.c57
-rw-r--r--portable/src/LCHAR.c355
-rw-r--r--portable/src/PANSIFileImpl.c449
-rw-r--r--portable/src/PANSIFileSystem.c52
-rw-r--r--portable/src/PANSIFileSystemImpl.c365
-rw-r--r--portable/src/PFile.c521
-rw-r--r--portable/src/PFileImpl.c199
-rw-r--r--portable/src/PFileSystem.c552
-rw-r--r--portable/src/PFileSystemImpl.c107
-rw-r--r--portable/src/PFileWrap.c513
-rw-r--r--portable/src/PStackSize.c61
-rw-r--r--portable/src/UNIX/PANSIFileSystemUNIXImpl.c166
-rw-r--r--portable/src/UNIX/PFileSystemUNIXImpl.c142
-rw-r--r--portable/src/UNIX/PFileWrapUNIX_OS_Specific.c116
-rw-r--r--portable/src/pLastError.c65
-rw-r--r--portable/src/pcputimer.c238
-rw-r--r--portable/src/pcrc.c159
-rw-r--r--portable/src/pendian.c49
-rw-r--r--portable/src/phashtable.c560
-rw-r--r--portable/src/plog.c538
-rw-r--r--portable/src/pmalloc.c610
-rw-r--r--portable/src/pmemblock.c544
-rw-r--r--portable/src/pmemfixed.c705
-rw-r--r--portable/src/pmemory.c962
-rw-r--r--portable/src/pmemory_ext.c369
-rw-r--r--portable/src/pmemory_ext.h47
-rw-r--r--portable/src/pstream.c990
-rw-r--r--portable/src/ptimer.c264
-rw-r--r--portable/src/ptimestamp.c56
-rw-r--r--portable/src/ptypes.c38
32 files changed, 10222 insertions, 0 deletions
diff --git a/portable/src/ArrayList.c b/portable/src/ArrayList.c
new file mode 100644
index 0000000..4c1127b
--- /dev/null
+++ b/portable/src/ArrayList.c
@@ -0,0 +1,132 @@
+/*---------------------------------------------------------------------------*
+ * ArrayList.c *
+ * *
+ * Copyright 2007, 2008 Nuance Communciations, Inc. *
+ * *
+ * 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 "ArrayList.h"
+#include "plog.h"
+#include "pmemory.h"
+
+
+ESR_ReturnCode ArrayListAdd(ArrayList* self, void* element)
+{
+ if (self == NULL)
+ {
+ PLogError(L("ESR_INVALID_ARGUMENT"));
+ return ESR_INVALID_ARGUMENT;
+ }
+ return self->add(self, element);
+}
+
+ESR_ReturnCode ArrayListInsertAt(ArrayList* self, size_t index, void* element)
+{
+ if (self == NULL)
+ {
+ PLogError(L("ESR_INVALID_ARGUMENT"));
+ return ESR_INVALID_ARGUMENT;
+ }
+ return self->insertAt(self, index, element);
+}
+
+ESR_ReturnCode ArrayListRemove(ArrayList* self, void* element)
+{
+ if (self == NULL)
+ {
+ PLogError(L("ESR_INVALID_ARGUMENT"));
+ return ESR_INVALID_ARGUMENT;
+ }
+ return self->remove(self, element);
+}
+
+ESR_ReturnCode ArrayListRemoveAtIndex(ArrayList* self, size_t index)
+{
+ if (self == NULL)
+ {
+ PLogError(L("ESR_INVALID_ARGUMENT"));
+ return ESR_INVALID_ARGUMENT;
+ }
+ return self->removeAtIndex(self, index);
+}
+
+ESR_ReturnCode ArrayListRemoveAll(ArrayList* self)
+{
+ if (self == NULL)
+ {
+ PLogError(L("ESR_INVALID_ARGUMENT"));
+ return ESR_INVALID_ARGUMENT;
+ }
+ return self->removeAll(self);
+}
+
+ESR_ReturnCode ArrayListContains(ArrayList* self, void* element, ESR_BOOL* exists)
+{
+ if (self == NULL)
+ {
+ PLogError(L("ESR_INVALID_ARGUMENT"));
+ return ESR_INVALID_ARGUMENT;
+ }
+ return self->contains(self, element, exists);
+}
+
+ESR_ReturnCode ArrayListGetSize(ArrayList* self, size_t* size)
+{
+ if (self == NULL)
+ {
+ PLogError(L("ESR_INVALID_ARGUMENT"));
+ return ESR_INVALID_ARGUMENT;
+ }
+ return self->getSize(self, size);
+}
+
+ESR_ReturnCode ArrayListGet(ArrayList* self, size_t index, void** element)
+{
+ if (self == NULL)
+ {
+ PLogError(L("ESR_INVALID_ARGUMENT"));
+ return ESR_INVALID_ARGUMENT;
+ }
+ return self->get(self, index, element);
+}
+
+ESR_ReturnCode ArrayListSet(ArrayList* self, size_t index, void* element)
+{
+ if (self == NULL)
+ {
+ PLogError(L("ESR_INVALID_ARGUMENT"));
+ return ESR_INVALID_ARGUMENT;
+ }
+ return self->set(self, index, element);
+}
+
+ESR_ReturnCode ArrayListClone(ArrayList* self, ArrayList* clone)
+{
+ if (self == NULL)
+ {
+ PLogError(L("ESR_INVALID_ARGUMENT"));
+ return ESR_INVALID_ARGUMENT;
+ }
+ return self->clone(self, clone);
+}
+
+ESR_ReturnCode ArrayListDestroy(ArrayList* self)
+{
+ if (self == NULL)
+ return ESR_INVALID_ARGUMENT;
+ return self->destroy(self);
+}
diff --git a/portable/src/ArrayListImpl.c b/portable/src/ArrayListImpl.c
new file mode 100644
index 0000000..0451e9c
--- /dev/null
+++ b/portable/src/ArrayListImpl.c
@@ -0,0 +1,241 @@
+/*---------------------------------------------------------------------------*
+ * ArrayListImpl.c *
+ * *
+ * Copyright 2007, 2008 Nuance Communciations, Inc. *
+ * *
+ * 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 "ArrayList.h"
+#include "ArrayListImpl.h"
+#include "pmemory.h"
+
+#define MTAG NULL
+#define INITIAL_CAPACITY 16
+
+ESR_ReturnCode ArrayListCreateWithCapacity(ArrayList **self, size_t minCapacity)
+{
+ ArrayListImpl* impl;
+
+ if (self == NULL)
+ return ESR_INVALID_ARGUMENT;
+
+ impl = NEW(ArrayListImpl, MTAG);
+
+ if (impl == NULL)
+ return ESR_OUT_OF_MEMORY;
+
+ impl->Interface.add = &ArrayList_Add;
+ impl->Interface.insertAt = &ArrayList_InsertAt;
+ impl->Interface.contains = &ArrayList_Contains;
+ impl->Interface.destroy = &ArrayList_Destroy;
+ impl->Interface.get = &ArrayList_Get;
+ impl->Interface.getSize = &ArrayList_GetSize;
+ impl->Interface.remove = &ArrayList_Remove;
+ impl->Interface.removeAtIndex = &ArrayList_RemoveAtIndex;
+ impl->Interface.removeAll = &ArrayList_RemoveAll;
+ impl->Interface.set = &ArrayList_Set;
+ impl->Interface.toStaticArray = NULL; /* Not implemented */
+ impl->Interface.clone = &ArrayList_Clone;
+
+ impl->contents = MALLOC(minCapacity * sizeof(void*), MTAG);
+ if (impl->contents == NULL)
+ {
+ FREE(impl);
+ return ESR_OUT_OF_MEMORY;
+ }
+ impl->capacity = minCapacity;
+ impl->minCapacity = minCapacity;
+ impl->size = 0;
+
+ *self = (ArrayList*) impl;
+ return ESR_SUCCESS;
+}
+
+
+ESR_ReturnCode ArrayListCreate(ArrayList** self)
+{
+ return ArrayListCreateWithCapacity(self, INITIAL_CAPACITY);
+}
+
+static ESR_ReturnCode ArrayList_Insert_Internal(ArrayListImpl *impl, size_t index, void *element)
+{
+ size_t i;
+
+ if (impl->size >= impl->capacity)
+ {
+ /* enlarge buffer */
+ size_t newCapacity = impl->capacity * 2;
+ void** temp = REALLOC(impl->contents, newCapacity * sizeof(void*));
+ if (temp == NULL)
+ return ESR_OUT_OF_MEMORY;
+ impl->contents = temp;
+ impl->capacity = newCapacity;
+ }
+
+ for (i = impl->size; i > index; --i)
+ impl->contents[i] = impl->contents[i - 1];
+ ++impl->size;
+ impl->contents[index] = element;
+ return ESR_SUCCESS;
+}
+
+ESR_ReturnCode ArrayList_Add(ArrayList* self, void* element)
+{
+ ArrayListImpl *impl = (ArrayListImpl *) self;
+
+ return ArrayList_Insert_Internal(impl, impl->size, element);
+}
+
+ESR_ReturnCode ArrayList_InsertAt(ArrayList *self, size_t index, void *element)
+{
+ ArrayListImpl *impl = (ArrayListImpl *) self;
+
+ if (index > impl->size)
+ return ESR_ARGUMENT_OUT_OF_BOUNDS;
+
+ return ArrayList_Insert_Internal(impl, index, element);
+}
+
+static ESR_ReturnCode ArrayList_Remove_Internal(ArrayListImpl *impl, size_t i)
+{
+ --impl->size;
+ while (i < impl->size)
+ {
+ impl->contents[i] = impl->contents[i+1];
+ ++i;
+ }
+
+ if (impl->capacity > impl->minCapacity &&
+ impl->size <= impl->capacity / 4)
+ {
+ void** temp;
+ size_t newCapacity = impl->capacity / 2;
+
+ /* shrink buffer */
+ if ((temp = REALLOC(impl->contents, newCapacity * sizeof(void*))) == NULL)
+ return ESR_OUT_OF_MEMORY;
+ impl->contents = temp;
+ impl->capacity = newCapacity;
+ }
+ return ESR_SUCCESS;
+}
+
+ESR_ReturnCode ArrayList_Remove(ArrayList* self, const void* element)
+{
+ ArrayListImpl* impl = (ArrayListImpl*) self;
+ size_t i;
+
+ /* Remove element */
+ for (i = 0; i < impl->size; ++i)
+ {
+ if (impl->contents[i] == element)
+ return ArrayList_Remove_Internal(impl, i);
+ }
+
+ return ESR_NO_MATCH_ERROR;
+}
+
+ESR_ReturnCode ArrayList_RemoveAtIndex(ArrayList* self, size_t index)
+{
+ ArrayListImpl* impl = (ArrayListImpl*) self;
+
+ if (index >= impl->size)
+ return ESR_ARGUMENT_OUT_OF_BOUNDS;
+
+ return ArrayList_Remove_Internal(impl, index);
+}
+
+ESR_ReturnCode ArrayList_RemoveAll(ArrayList* self)
+{
+ ArrayListImpl* impl = (ArrayListImpl*) self;
+
+ impl->size = 0;
+ return ESR_SUCCESS;
+}
+
+ESR_ReturnCode ArrayList_Contains(ArrayList* self, const void* element,
+ ESR_BOOL* exists)
+{
+ ArrayListImpl* impl = (ArrayListImpl*) self;
+ size_t i;
+
+ for (i = 0; i < impl->size; ++i)
+ {
+ if (impl->contents[i] == element)
+ {
+ *exists = ESR_TRUE;
+ return ESR_SUCCESS;
+ }
+ }
+ *exists = ESR_FALSE;
+ return ESR_SUCCESS;
+}
+
+ESR_ReturnCode ArrayList_Get(ArrayList* self, size_t index, void** element)
+{
+ ArrayListImpl* impl = (ArrayListImpl*) self;
+
+ if (index >= impl->size)
+ return ESR_ARGUMENT_OUT_OF_BOUNDS;
+ *element = impl->contents[index];
+ return ESR_SUCCESS;
+}
+
+ESR_ReturnCode ArrayList_Set(ArrayList* self, size_t index, void* element)
+{
+ ArrayListImpl* impl = (ArrayListImpl*) self;
+
+ if (index >= impl->size)
+ return ESR_ARGUMENT_OUT_OF_BOUNDS;
+ impl->contents[index] = element;
+ return ESR_SUCCESS;
+}
+
+ESR_ReturnCode ArrayList_GetSize(ArrayList* self, size_t* size)
+{
+ ArrayListImpl* impl = (ArrayListImpl*) self;
+
+ *size = impl->size;
+ return ESR_SUCCESS;
+}
+
+ESR_ReturnCode ArrayList_Clone(ArrayList* self, ArrayList* clone)
+{
+ size_t size, i;
+ void* element;
+ ESR_ReturnCode rc;
+
+ CHK(rc, clone->removeAll(clone));
+ CHK(rc, self->getSize(self, &size));
+ for (i = 0; i < size; ++i)
+ {
+ CHK(rc, self->get(self, i, &element));
+ CHK(rc, clone->add(clone, element));
+ }
+ return ESR_SUCCESS;
+CLEANUP:
+ return rc;
+}
+
+ESR_ReturnCode ArrayList_Destroy(ArrayList* self)
+{
+ ArrayListImpl* impl = (ArrayListImpl*) self;
+
+ FREE(impl->contents);
+ FREE(self);
+ return ESR_SUCCESS;
+}
diff --git a/portable/src/ESR_ReturnCode.c b/portable/src/ESR_ReturnCode.c
new file mode 100644
index 0000000..aed93ca
--- /dev/null
+++ b/portable/src/ESR_ReturnCode.c
@@ -0,0 +1,57 @@
+/*---------------------------------------------------------------------------*
+ * ESR_ReturnCode.c *
+ * *
+ * Copyright 2007, 2008 Nuance Communciations, Inc. *
+ * *
+ * 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 "ESR_ReturnCode.h"
+
+const LCHAR* ESR_rc2str(const ESR_ReturnCode rc)
+{
+ switch (rc) {
+ case ESR_SUCCESS: return L("ESR_SUCCESS");
+ case ESR_CONTINUE_PROCESSING: return L("ESR_CONTINUE_PROCESSING");
+ case ESR_FATAL_ERROR: return L("ESR_FATAL_ERROR");
+ case ESR_BUFFER_OVERFLOW: return L("ESR_BUFFER_OVERFLOW");
+ case ESR_OPEN_ERROR: return L("ESR_OPEN_ERROR");
+ case ESR_ALREADY_OPEN: return L("ESR_ALREADY_OPEN");
+ case ESR_CLOSE_ERROR: return L("ESR_CLOSE_ERROR");
+ case ESR_ALREADY_CLOSED: return L("ESR_ALREADY_CLOSED");
+ case ESR_READ_ERROR: return L("ESR_READ_ERROR");
+ case ESR_WRITE_ERROR: return L("ESR_WRITE_ERROR");
+ case ESR_FLUSH_ERROR: return L("ESR_FLUSH_ERROR");
+ case ESR_SEEK_ERROR: return L("ESR_SEEK_ERROR");
+ case ESR_OUT_OF_MEMORY: return L("ESR_OUT_OF_MEMORY");
+ case ESR_ARGUMENT_OUT_OF_BOUNDS: return L("ESR_ARGUMENT_OUT_OF_BOUNDS");
+ case ESR_NO_MATCH_ERROR: return L("ESR_NO_MATCH_ERROR");
+ case ESR_INVALID_ARGUMENT: return L("ESR_INVALID_ARGUMENT");
+ case ESR_NOT_SUPPORTED: return L("ESR_NOT_SUPPORTED");
+ case ESR_INVALID_STATE: return L("ESR_INVALID_STATE");
+ case ESR_THREAD_CREATION_ERROR: return L("ESR_THREAD_CREATION_ERROR");
+ case ESR_IDENTIFIER_COLLISION: return L("ESR_IDENTIFIER_COLLISION");
+ case ESR_TIMED_OUT: return L("ESR_TIMED_OUT");
+ case ESR_INVALID_RESULT_TYPE: return L("ESR_INVALID_RESULT_TYPE");
+ case ESR_NOT_IMPLEMENTED: return L("ESR_NOT_IMPLEMENTED");
+ case ESR_CONNECTION_RESET_BY_PEER: return L("ESR_CONNECTION_RESET_BY_PEER");
+ case ESR_PROCESS_CREATE_ERROR: return L("ESR_PROCESS_CREATE_ERROR");
+ case ESR_TTS_NO_ENGINE: return L("ESR_TTS_NO_ENGINE");
+ case ESR_MUTEX_CREATION_ERROR: return L("ESR_MUTEX_CREATION_ERROR");
+ case ESR_DEADLOCK: return L("ESR_DEADLOCK");
+ };
+ return L("invalid return code");
+}
diff --git a/portable/src/LCHAR.c b/portable/src/LCHAR.c
new file mode 100644
index 0000000..d17e0e8
--- /dev/null
+++ b/portable/src/LCHAR.c
@@ -0,0 +1,355 @@
+/*---------------------------------------------------------------------------*
+ * LCHAR.c *
+ * *
+ * Copyright 2007, 2008 Nuance Communciations, Inc. *
+ * *
+ * 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 "LCHAR.h"
+#include "plog.h"
+#include "pmemory.h"
+
+#define MTAG NULL
+
+ESR_ReturnCode lstrtrim(LCHAR* text)
+{
+ size_t beginning, ending, len;
+
+ len = LSTRLEN(text);
+
+ /* locating first non-whitespace character from beginning */
+ for (beginning = 0; beginning < len && LISSPACE(text[beginning]); ++beginning);
+ /* locating first non-whitespace character from end */
+ for (ending = len - 1; ending > beginning && LISSPACE(text[ending]); --ending);
+
+ if (beginning > 0 && beginning <= ending)
+ LMEMMOVE(text, text + beginning, ending - beginning + 1);
+ text[ending-beginning+1] = '\0';
+ return ESR_SUCCESS;
+}
+
+ESR_ReturnCode lstrinsert(const LCHAR* source, LCHAR* target, size_t offset, size_t* len)
+{
+ ESR_ReturnCode rc;
+
+ if (source == NULL || target == NULL || len == NULL)
+ {
+ rc = ESR_INVALID_ARGUMENT;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ if (LSTRLEN(source) + LSTRLEN(target) + 1 > *len)
+ {
+ *len = LSTRLEN(source) + LSTRLEN(target) + 1;
+ rc = ESR_BUFFER_OVERFLOW;
+ PLOG_DBG_TRACE((ESR_rc2str(rc)));
+ goto CLEANUP;
+ }
+ memmove(target + offset + LSTRLEN(source), target + offset, LSTRLEN(target + offset) + 1);
+ LSTRNCPY(target + offset, source, LSTRLEN(source));
+ return ESR_SUCCESS;
+CLEANUP:
+ return rc;
+}
+
+ESR_ReturnCode lstrreplace(LCHAR* text, const LCHAR source, const LCHAR target)
+{
+ LCHAR* index;
+
+ while (ESR_TRUE)
+ {
+ index = LSTRCHR(text, source);
+ if (index == NULL)
+ break;
+ *index = target;
+ }
+ return ESR_SUCCESS;
+}
+
+ESR_ReturnCode lstrtoi(const LCHAR* text, int* result, int base)
+{
+ LCHAR* endPtr;
+
+ if (result == NULL)
+ return ESR_INVALID_ARGUMENT;
+ *result = LSTRTOL(text, &endPtr, base);
+ if (endPtr == text || (!LISSPACE(*endPtr) && *endPtr != L('\0')))
+ return ESR_INVALID_ARGUMENT;
+ return ESR_SUCCESS;
+}
+
+ESR_ReturnCode lstrtoui(const LCHAR* text, unsigned int* result, int base)
+{
+ LCHAR* endPtr;
+
+ if (result == NULL)
+ return ESR_INVALID_ARGUMENT;
+ *result = LSTRTOUL(text, &endPtr, base);
+ if (endPtr == text || (!LISSPACE(*endPtr) && *endPtr != L('\0')))
+ return ESR_INVALID_ARGUMENT;
+ return ESR_SUCCESS;
+}
+
+ESR_ReturnCode lstrtof(const LCHAR* text, float* result)
+{
+ LCHAR* endPtr;
+
+ if (result == NULL)
+ return ESR_INVALID_ARGUMENT;
+ *result = (float) LSTRTOD(text, &endPtr);
+ if (endPtr == text || (!LISSPACE(*endPtr) && *endPtr != L('\0')))
+ return ESR_INVALID_ARGUMENT;
+ return ESR_SUCCESS;
+}
+
+ESR_ReturnCode lstrtob(const LCHAR* text, ESR_BOOL* result)
+{
+ ESR_ReturnCode rc = ESR_SUCCESS;
+ int compare;
+ unsigned int temp;
+
+ if (result == NULL)
+ return ESR_INVALID_ARGUMENT;
+ CHKLOG(rc, lstrcasecmp(text, L("true"), &compare));
+ if (compare == 0)
+ {
+ *result = ESR_TRUE;
+ return ESR_SUCCESS;
+ }
+ CHKLOG(rc, lstrcasecmp(text, L("yes"), &compare));
+ if (compare == 0)
+ {
+ *result = ESR_TRUE;
+ return ESR_SUCCESS;
+ }
+ CHKLOG(rc, lstrcasecmp(text, L("false"), &compare));
+ if (compare == 0)
+ {
+ *result = ESR_FALSE;
+ return ESR_SUCCESS;
+ }
+ CHKLOG(rc, lstrcasecmp(text, L("no"), &compare));
+ if (compare == 0)
+ {
+ *result = ESR_FALSE;
+ return ESR_SUCCESS;
+ }
+
+ /* Check for boolean expressed as an integer value */
+ CHK(rc, lstrtoui(text, &temp, 10));
+ *result = (temp != 0);
+ return ESR_SUCCESS;
+CLEANUP:
+ return rc;
+}
+
+ESR_ReturnCode LCHARGetInt( LCHAR* text, int* value, LCHAR** finalPosition)
+{
+ LCHAR *beg, *end;
+ LCHAR temp;
+ ESR_ReturnCode rc;
+
+ /* Skip whitespace */
+ for (beg = text; *beg != L('\0') && LISSPACE(*beg); ++beg);
+ if (beg == NULL)
+ return ESR_INVALID_ARGUMENT; /* invalid command syntax */
+ /* Find next whitespace */
+ for (end = beg; *end != L('\0') && !LISSPACE(*end); ++end);
+ if (end == NULL)
+ return ESR_INVALID_ARGUMENT; /* invalid command syntax */
+
+ temp = *end;
+ *end = L('\0');
+ rc = lstrtoi(beg, value, 10);
+ if (rc != ESR_SUCCESS)
+ {
+ *end = temp;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ *end = temp;
+ if (finalPosition != NULL)
+ *finalPosition = end;
+ return ESR_SUCCESS;
+CLEANUP:
+ return rc;
+}
+
+ESR_ReturnCode lstrlwr(LCHAR* string)
+{
+ if (string)
+ {
+ while (*string)
+ {
+ if (LISALPHA(*string))
+ *string = (LCHAR) LTOLOWER(*string);
+ ++string;
+ }
+ }
+ else
+ return ESR_INVALID_ARGUMENT;
+
+ return ESR_SUCCESS;
+}
+
+ESR_ReturnCode lstrupr(LCHAR* string)
+{
+ if (string)
+ {
+ while (*string)
+ {
+ if (LISALPHA(*string))
+ *string = (LCHAR) LTOUPPER(*string);
+ ++string;
+ }
+ }
+ else
+ return ESR_INVALID_ARGUMENT;
+
+ return ESR_SUCCESS;
+}
+
+/* strcasecmp is not POSIX.4 API */
+ESR_ReturnCode lstrcasecmp(const LCHAR *string1, const LCHAR *string2, int *result)
+{
+
+ if (!string1 || !string2)
+ return ESR_INVALID_ARGUMENT;
+
+ while (LTOUPPER(*string1) == LTOUPPER(*string2++))
+ {
+ if (!*string1++)
+ {
+ *result = 0;
+ return ESR_SUCCESS;
+ }
+ }
+
+ *result = LTOUPPER(*string1) - LTOUPPER(*--string2);
+ return ESR_SUCCESS;
+}
+
+/**
+ * This code is from MS SDK: C:\PROGRAM FILES\MICROSOFT SDK\src\crt\xtoa.c
+ * Buffer overflow checking is left up to the caller.
+ *
+ * @param value Number to be converted
+ * @param string String result
+ * @param radix Base of value; must be in the range 2 - 36
+ */
+static void pxtoa(unsigned long val, LCHAR *buf, unsigned radix, int is_neg)
+{
+ LCHAR *p; /* pointer to traverse string */
+ LCHAR *firstdig; /* pointer to first digit */
+ LCHAR temp; /* temp char */
+ unsigned digval; /* value of digit */
+
+ p = buf;
+
+ if (is_neg)
+ {
+ /* negative, so output '-' and negate */
+ *p++ = '-';
+ val = (unsigned long)(-(long)val);
+ }
+
+ firstdig = p; /* save pointer to first digit */
+
+ do
+ {
+ digval = (unsigned)(val % radix);
+ val /= radix; /* get next digit */
+
+ /* convert to ascii and store */
+ if (digval > 9)
+ *p++ = (LCHAR)(digval - 10 + 'a'); /* a letter */
+ else
+ *p++ = (LCHAR)(digval + '0'); /* a digit */
+ }
+ while (val > 0);
+
+ /* We now have the digit of the number in the buffer, but in reverse
+ order. Thus we reverse them now. */
+
+ *p-- = '\0'; /* terminate string; p points to last digit */
+
+ do
+ {
+ temp = *p;
+ *p = *firstdig;
+ *firstdig = temp; /* swap *p and *firstdig */
+ --p;
+ ++firstdig; /* advance to next two digits */
+ }
+ while (firstdig < p); /* repeat until halfway */
+}
+
+/*
+ * Convert an integer to a string.
+ */
+ESR_ReturnCode litostr(int value, LCHAR *string, size_t *len, int radix)
+{
+ size_t size;
+ /* pxtoa() is guaranteed not to overflow past 33 characters */
+ LCHAR buffer[33];
+
+ if (!string)
+ return ESR_INVALID_ARGUMENT;
+
+ if (radix == 10 && value < 0)
+ pxtoa((unsigned long) value, buffer, radix, 1);
+ else
+ pxtoa((unsigned long) value, buffer, radix, 0);
+
+ size = LSTRLEN(buffer);
+
+ if (size >= *len) /* + null-terminated character */
+ {
+ *len = size;
+ return ESR_BUFFER_OVERFLOW;
+ }
+ else
+ LSTRCPY(string, buffer);
+
+ return ESR_SUCCESS;
+}
+
+
+/* Convert an unsigned long integer to a string. */
+ESR_ReturnCode lultostr(unsigned long value, LCHAR *string, size_t *len, int radix)
+{
+ size_t size;
+ LCHAR buffer[33];
+
+ if (!string)
+ return ESR_INVALID_ARGUMENT;
+
+ pxtoa(value, buffer, radix, 0);
+
+ size = LSTRLEN(buffer);
+
+ if (size >= *len) /* + null-terminated character */
+ {
+ *len = size;
+ return ESR_BUFFER_OVERFLOW;
+ }
+ else
+ {
+ *len = size;
+ LSTRCPY(string, buffer);
+ }
+
+ return ESR_SUCCESS;
+}
diff --git a/portable/src/PANSIFileImpl.c b/portable/src/PANSIFileImpl.c
new file mode 100644
index 0000000..6d9a261
--- /dev/null
+++ b/portable/src/PANSIFileImpl.c
@@ -0,0 +1,449 @@
+/*---------------------------------------------------------------------------*
+ * PANSIFileImpl.c *
+ * *
+ * Copyright 2007, 2008 Nuance Communciations, Inc. *
+ * *
+ * 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 "errno.h"
+#include "passert.h"
+#include "pendian.h"
+#include "PFileImpl.h"
+#include "PANSIFileImpl.h"
+#include "PFileSystem.h"
+#include "ESR_ReturnCode.h"
+#include "plog.h"
+#include "pmemory.h"
+#include "pstdio.h"
+#include "ptypes.h"
+
+#define MTAG NULL
+
+ESR_ReturnCode PANSIFileCreateImpl(const LCHAR* filename, ESR_BOOL isLittleEndian, PFile** self)
+{
+ PANSIFileImpl* impl = NULL;
+ ESR_ReturnCode rc;
+
+ impl = NEW(PANSIFileImpl, MTAG);
+ if (impl == NULL)
+ {
+ rc = ESR_OUT_OF_MEMORY;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+
+ PFileCreateImpl(&impl->Interface.Interface, filename, isLittleEndian);
+ impl->Interface.Interface.close = &PANSIFileCloseImpl;
+ impl->Interface.Interface.clearError = &PANSIFileClearErrorImpl;
+ impl->Interface.Interface.destroy = &PANSIFileDestroyImpl;
+ impl->Interface.Interface.fgetc = &PANSIFileFgetcImpl;
+ impl->Interface.Interface.fgets = &PANSIFileFgetsImpl;
+ impl->Interface.Interface.getPosition = &PANSIFileGetPositionImpl;
+ impl->Interface.Interface.hideMemoryAllocation = &PANSIFileHideMemoryAllocation;
+ impl->Interface.Interface.isEOF = &PANSIFileIsEOFImpl;
+ impl->Interface.Interface.isErrorSet = &PANSIFileIsErrorSetImpl;
+ impl->Interface.Interface.isOpen = &PANSIFileIsOpenImpl;
+ impl->Interface.Interface.open = &PANSIFileOpenImpl;
+ impl->Interface.Interface.read = &PANSIFileReadImpl;
+ impl->Interface.Interface.seek = &PANSIFileSeekImpl;
+ impl->Interface.Interface.flush = &PANSIFileFlushImpl;
+ impl->Interface.Interface.write = &PANSIFileWriteImpl;
+
+ impl->Interface.filename[0] = 0;
+ impl->value = NULL;
+
+ LSTRCAT(impl->Interface.filename, filename);
+ *self = &impl->Interface.Interface;
+ return ESR_SUCCESS;
+CLEANUP:
+ if (impl != NULL)
+ impl->Interface.Interface.destroy(&impl->Interface.Interface);
+ return rc;
+}
+
+ESR_ReturnCode PANSIFileDestroyImpl(PFile* self)
+{
+ ESR_ReturnCode rc;
+
+ CHK(rc, PFileDestroyImpl(self));
+ FREE(self);
+ return ESR_SUCCESS;
+CLEANUP:
+ return rc;
+}
+
+
+#ifdef USE_THREAD
+#define LOCK_MUTEX(rc, impl) \
+ if (impl->Interface.lock != NULL) \
+ CHKLOG(rc, PtrdMonitorLock(impl->Interface.lock));
+#else
+#define LOCK_MUTEX(rc, impl)
+#endif
+
+
+#ifdef USE_THREAD
+#define CLEANUP_AND_RETURN(rc, impl) \
+ if (impl->Interface.lock!=NULL) \
+ CHKLOG(rc, PtrdMonitorUnlock(impl->Interface.lock)); \
+ return ESR_SUCCESS; \
+ CLEANUP: \
+ if (impl->Interface.lock!=NULL) \
+ PtrdMonitorUnlock(impl->Interface.lock); \
+ return rc;
+#else
+#define CLEANUP_AND_RETURN(rc, impl) \
+ return ESR_SUCCESS; \
+ CLEANUP: \
+ return rc;
+#endif
+
+
+ESR_ReturnCode PANSIFileOpenImpl(PFile* self, const LCHAR* mode)
+{
+ PANSIFileImpl* impl = (PANSIFileImpl*) self;
+ ESR_ReturnCode rc;
+
+ LOCK_MUTEX(rc, impl);
+ if (impl->value != NULL)
+ {
+ rc = ESR_ALREADY_OPEN;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ impl->value = fopen(impl->Interface.filename, mode);
+
+ if (impl->value == NULL)
+ {
+ LCHAR path[P_PATH_MAX];
+ size_t len;
+
+ len = P_PATH_MAX;
+ CHKLOG(rc, PFileSystemGetcwd(path, &len));
+ rc = ESR_OPEN_ERROR;
+ /* PLOG_DBG_TRACE((L("%s: filename=%s, cwd=%s"), ESR_rc2str(rc), impl->Interface.filename, path)); */
+ PLogError(L("%s: filename=%s, cwd=%s"), ESR_rc2str(rc), impl->Interface.filename, path);
+ goto CLEANUP;
+ }
+ CLEANUP_AND_RETURN(rc, impl);
+}
+
+ESR_ReturnCode PANSIFileCloseImpl(PFile* self)
+{
+ PANSIFileImpl* impl = (PANSIFileImpl*) self;
+ ESR_ReturnCode rc;
+
+ LOCK_MUTEX(rc, impl);
+ if (fclose(impl->value) != 0)
+ {
+ rc = ESR_CLOSE_ERROR;
+ PLogMessage(L("%s: file %s, handle"), ESR_rc2str(rc), impl->Interface.filename, impl->value);
+ goto CLEANUP;
+ }
+ impl->value = NULL;
+ CLEANUP_AND_RETURN(rc, impl);
+}
+
+ESR_ReturnCode PANSIFileReadImpl(PFile* self, void* buffer, size_t size, size_t* count)
+{
+ PANSIFileImpl* impl = (PANSIFileImpl*) self;
+ ESR_ReturnCode rc;
+
+ LOCK_MUTEX(rc, impl);
+ if (count == NULL)
+ {
+ rc = ESR_INVALID_ARGUMENT;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+
+ if (size != 0 && *count != 0)
+ {
+ ESR_BOOL needToSwap;
+
+ *count = fread(buffer, size, *count, impl->value);
+ if (*count == 0 && ferror(impl->value))
+ {
+ rc = ESR_READ_ERROR;
+ PLogMessage(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+
+#ifdef __LITTLE_ENDIAN
+ needToSwap = !impl->Interface.littleEndian;
+#else
+ needToSwap = impl->Interface.littleEndian;
+#endif
+
+ if (needToSwap)
+ swap_byte_order(buffer, *count, size);
+ }
+ else
+ *count = 0;
+ CLEANUP_AND_RETURN(rc, impl);
+}
+
+ESR_ReturnCode PANSIFileWriteImpl(PFile* self, void* buffer, size_t size, size_t* count)
+{
+ PANSIFileImpl* impl = (PANSIFileImpl*) self;
+ ESR_ReturnCode rc;
+ size_t requested = *count;
+
+ LOCK_MUTEX(rc, impl);
+ if (count == NULL)
+ {
+ rc = ESR_INVALID_ARGUMENT;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ if (size != 0 && *count != 0)
+ {
+ ESR_BOOL needToSwap;
+ void* temp;
+
+#ifdef __LITTLE_ENDIAN
+ needToSwap = !impl->Interface.littleEndian;
+#else
+ needToSwap = impl->Interface.littleEndian;
+#endif
+ if (needToSwap)
+ {
+ temp = MALLOC(*count * size, MTAG);
+ if (temp == NULL)
+ {
+ rc = ESR_OUT_OF_MEMORY;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ memcpy(temp, buffer, *count * size);
+
+ swap_byte_order(temp, *count, size);
+ }
+ else
+ temp = buffer;
+
+ *count = fwrite(temp, size, *count, impl->value);
+ if (needToSwap)
+ {
+ FREE(temp);
+ temp = NULL;
+ }
+
+ if (*count < requested)
+ {
+ rc = ESR_WRITE_ERROR;
+ PLogMessage(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ }
+ else
+ *count = 0;
+ CLEANUP_AND_RETURN(rc, impl);
+}
+
+ESR_ReturnCode PANSIFileFlushImpl(PFile* self)
+{
+ PANSIFileImpl* impl = (PANSIFileImpl*) self;
+ ESR_ReturnCode rc;
+
+ LOCK_MUTEX(rc, impl);
+ if (fflush(impl->value) != 0)
+ {
+ rc = ESR_FLUSH_ERROR;
+ PLogMessage(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ CLEANUP_AND_RETURN(rc, impl);
+}
+
+ESR_ReturnCode PANSIFileSeekImpl(PFile* self, long offset, int origin)
+{
+ PANSIFileImpl* impl = (PANSIFileImpl*) self;
+ ESR_ReturnCode rc;
+
+ LOCK_MUTEX(rc, impl);
+ if (fseek(impl->value, offset, origin) != 0)
+ {
+ rc = ESR_SEEK_ERROR;
+ PLogMessage(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ CLEANUP_AND_RETURN(rc, impl);
+}
+
+ESR_ReturnCode PANSIFileGetPositionImpl(PFile* self, size_t* position)
+{
+ PANSIFileImpl* impl = (PANSIFileImpl*) self;
+ ESR_ReturnCode rc;
+ long pos;
+
+ LOCK_MUTEX(rc, impl);
+ pos = ftell(impl->value);
+ if (pos == -1)
+ {
+ switch (errno)
+ {
+ case EBADF:
+ rc = ESR_INVALID_STATE;
+ PLogError(L("%s: Got EBADF"), rc);
+ goto CLEANUP;
+ case EINVAL:
+ rc = ESR_INVALID_STATE;
+ PLogError(L("%s: Got EINVAL"), rc);
+ goto CLEANUP;
+ default:
+ rc = ESR_INVALID_STATE;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ }
+ *position = pos;
+ CLEANUP_AND_RETURN(rc, impl);
+}
+
+ESR_ReturnCode PANSIFileIsOpenImpl(PFile* self, ESR_BOOL* isOpen)
+{
+ PANSIFileImpl* impl = (PANSIFileImpl*) self;
+ ESR_ReturnCode rc;
+
+ LOCK_MUTEX(rc, impl);
+ if (isOpen == NULL)
+ {
+ rc = ESR_INVALID_ARGUMENT;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ *isOpen = impl->value != NULL;
+ CLEANUP_AND_RETURN(rc, impl);
+}
+
+ESR_ReturnCode PANSIFileIsEOFImpl(PFile* self, ESR_BOOL* isEof)
+{
+ PANSIFileImpl* impl = (PANSIFileImpl*) self;
+ ESR_ReturnCode rc;
+
+ LOCK_MUTEX(rc, impl);
+ if (isEof == NULL)
+ {
+ rc = ESR_INVALID_ARGUMENT;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+#ifdef NO_FEOF
+ {
+ long posCur; /* remember current file position */
+ long posEnd; /* end of file position */
+
+ posCur = ftell(impl->value);
+ fseek(impl->value, 0, SEEK_END);
+ posEnd = ftell(impl->value);
+ *isEof = (posCur == posEnd);
+ fseek(impl->value, posCur, SEEK_SET); /* restore position in file */
+ }
+#else
+ *isEof = feof(impl->value) != 0;
+#endif
+ CLEANUP_AND_RETURN(rc, impl);
+}
+
+ESR_ReturnCode PANSIFileIsErrorSetImpl(PFile* self, ESR_BOOL* isError)
+{
+ PANSIFileImpl* impl = (PANSIFileImpl*) self;
+ ESR_ReturnCode rc;
+
+ LOCK_MUTEX(rc, impl);
+ if (isError == NULL)
+ {
+ rc = ESR_INVALID_ARGUMENT;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ *isError = ferror(impl->value) != 0;
+ CLEANUP_AND_RETURN(rc, impl);
+}
+
+ESR_ReturnCode PANSIFileClearErrorImpl(PFile* self)
+{
+ PANSIFileImpl* impl = (PANSIFileImpl*) self;
+ ESR_ReturnCode rc;
+
+ LOCK_MUTEX(rc, impl);
+ clearerr(impl->value);
+ CLEANUP_AND_RETURN(rc, impl);
+}
+
+ESR_ReturnCode PANSIFileFgetsImpl(PFile* self, LCHAR* string, int n, LCHAR** result)
+{
+ PANSIFileImpl* impl = (PANSIFileImpl*) self;
+ ESR_ReturnCode rc;
+ LCHAR* temp;
+
+ LOCK_MUTEX(rc, impl);
+ temp = fgets(string, n, impl->value);
+ if (result != NULL)
+ *result = temp;
+ if (temp == NULL && ferror(impl->value))
+ {
+ rc = ESR_INVALID_STATE;
+ PLogMessage(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ CLEANUP_AND_RETURN(rc, impl);
+}
+
+ESR_ReturnCode PANSIFileFgetcImpl(PFile* self, LINT* result)
+{
+ PANSIFileImpl* impl = (PANSIFileImpl*) self;
+ ESR_ReturnCode rc;
+
+ LOCK_MUTEX(rc, impl);
+ *result = fgetc(impl->value);
+ if (*result == PEOF && ferror(impl->value))
+ {
+ rc = ESR_INVALID_STATE;
+ PLogMessage(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ CLEANUP_AND_RETURN(rc, impl);
+}
+
+ESR_ReturnCode PANSIFileHideMemoryAllocation(PFile* self)
+{
+ PANSIFileImpl* impl = (PANSIFileImpl*) self;
+ ESR_ReturnCode rc;
+
+ LOCK_MUTEX(rc, impl);
+ rc = PMemLogFree(self);
+ if (rc != ESR_SUCCESS)
+ {
+ pfprintf(PSTDERR, L("%s: PMemDumpLogFile() at %s:%d"), ESR_rc2str(rc), __FILE__, __LINE__);
+ goto CLEANUP;
+ }
+ rc = PMemLogFree(impl->Interface.filename);
+ if (rc != ESR_SUCCESS)
+ {
+ pfprintf(PSTDERR, L("%s: PMemDumpLogFile() at %s:%d"), ESR_rc2str(rc), __FILE__, __LINE__);
+ goto CLEANUP;
+ }
+#ifdef USE_THREAD
+ rc = PMemLogFree(impl->Interface.lock);
+ if (rc != ESR_SUCCESS)
+ {
+ pfprintf(PSTDERR, L("%s: PMemDumpLogFile() at %s:%d"), ESR_rc2str(rc), __FILE__, __LINE__);
+ goto CLEANUP;
+ }
+#endif
+ CLEANUP_AND_RETURN(rc, impl);
+}
diff --git a/portable/src/PANSIFileSystem.c b/portable/src/PANSIFileSystem.c
new file mode 100644
index 0000000..70c7b5d
--- /dev/null
+++ b/portable/src/PANSIFileSystem.c
@@ -0,0 +1,52 @@
+/*---------------------------------------------------------------------------*
+ * PANSIFileSystem.c *
+ * *
+ * Copyright 2007, 2008 Nuance Communciations, Inc. *
+ * *
+ * 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 "LCHAR.h"
+#include "PANSIFileSystemImpl.h"
+#include "plog.h"
+
+extern PFileSystem* PANSIFileSystemSingleton;
+
+ESR_ReturnCode PANSIFileSystemAddPath(const LCHAR* virtualPath, const LCHAR* realPath)
+{
+ return ((PANSIFileSystem*) PANSIFileSystemSingleton)->addPath(PANSIFileSystemSingleton, virtualPath, realPath);
+}
+
+ESR_ReturnCode PANSIFileSystemRemovePath(const LCHAR* virtualPath)
+{
+ return ((PANSIFileSystem*) PANSIFileSystemSingleton)->removePath(PANSIFileSystemSingleton, virtualPath);
+}
+
+ESR_ReturnCode PANSIFileSystemGetcwd(LCHAR* path, size_t* len)
+{
+ return ((PANSIFileSystem*) PANSIFileSystemSingleton)->getcwd(PANSIFileSystemSingleton, path, len);
+}
+
+ESR_ReturnCode PANSIFileSystemDestroy(void)
+{
+ ESR_ReturnCode rc;
+
+ if (PANSIFileSystemSingleton == NULL)
+ return ESR_SUCCESS;
+ CHKLOG(rc, PANSIFileSystemSingleton->destroy(PANSIFileSystemSingleton));
+ PANSIFileSystemSingleton = NULL;
+ return ESR_SUCCESS;
+CLEANUP:
+ return rc;
+}
diff --git a/portable/src/PANSIFileSystemImpl.c b/portable/src/PANSIFileSystemImpl.c
new file mode 100644
index 0000000..cbe74b3
--- /dev/null
+++ b/portable/src/PANSIFileSystemImpl.c
@@ -0,0 +1,365 @@
+/*---------------------------------------------------------------------------*
+ * PANSIFileSystemImpl.c *
+ * *
+ * Copyright 2007, 2008 Nuance Communciations, Inc. *
+ * *
+ * 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 "LCHAR.h"
+#include "PFileSystemImpl.h"
+#include "PANSIFileSystemImpl.h"
+#include "PANSIFileImpl.h"
+#include "plog.h"
+#include "pmemory.h"
+
+//extern PFileSystem* PANSIFileSystemSingleton;
+PFileSystem* PANSIFileSystemSingleton = (PFileSystem*)NULL;
+
+#define MTAG NULL
+
+
+#ifdef USE_THREAD
+/* Prototype of private function */
+PORTABLE_API ESR_ReturnCode PtrdFlush();
+#endif
+
+/**
+ * [file path, PFileSystem*] mapping.
+ */
+extern PHashTable* PFileSystemPathMap;
+
+
+ESR_ReturnCode PANSIFileSystemCreate(void)
+{
+ PANSIFileSystemImpl* impl;
+ ESR_ReturnCode rc;
+
+ if (PANSIFileSystemSingleton != NULL)
+ return ESR_SUCCESS;
+ impl = NEW(PANSIFileSystemImpl, MTAG);
+ if (impl == NULL)
+ return ESR_OUT_OF_MEMORY;
+ impl->super.super.destroy = &PANSIFileSystemDestroyImpl;
+ impl->super.super.createPFile = &PANSIFileSystemCreatePFileImpl;
+ impl->super.addPath = &PANSIFileSystemAddPathImpl;
+ impl->super.removePath = &PANSIFileSystemRemovePathImpl;
+ impl->super.getcwd = &PANSIFileSystemGetcwdImpl;
+ impl->super.super.mkdir = &PANSIFileSystemMkdirImpl;
+ impl->super.super.chdir = &PANSIFileSystemChdirImpl;
+
+ CHKLOG(rc, PHashTableCreate(NULL, MTAG, &impl->directoryMap));
+ PANSIFileSystemSingleton = &impl->super.super;
+ return ESR_SUCCESS;
+CLEANUP:
+ return rc;
+}
+
+ESR_ReturnCode PANSIFileSystemDestroyImpl(PFileSystem* self)
+{
+ PANSIFileSystemImpl* impl = (PANSIFileSystemImpl*) self;
+ PHashTableEntry* entry;
+ PHashTableEntry* oldEntry;
+ LCHAR* key;
+ LCHAR* value;
+ ESR_ReturnCode rc;
+
+ if (impl->directoryMap != NULL)
+ {
+ CHKLOG(rc, PHashTableEntryGetFirst(impl->directoryMap, &entry));
+ while (entry != NULL)
+ {
+ CHKLOG(rc, PHashTableEntryGetKeyValue(entry, (void **)&key, (void **)&value));
+ oldEntry = entry;
+ CHKLOG(rc, PHashTableEntryAdvance(&entry));
+ CHKLOG(rc, PHashTableEntryRemove(oldEntry));
+ CHKLOG(rc, PHashTableRemoveValue(PFileSystemPathMap, key, NULL));
+ FREE(key);
+ FREE(value);
+ }
+ CHKLOG(rc, PHashTableDestroy(impl->directoryMap));
+ impl->directoryMap = NULL;
+ }
+ FREE(self);
+ return ESR_SUCCESS;
+CLEANUP:
+ return rc;
+}
+
+ESR_ReturnCode PANSIFileSystemAddPathImpl(PFileSystem* self, const LCHAR* virtualPath, const LCHAR* realPath)
+{
+ PANSIFileSystemImpl* impl = (PANSIFileSystemImpl*) self;
+ ESR_BOOL exists;
+ LCHAR* key = NULL;
+ LCHAR* value = NULL;
+ ESR_ReturnCode rc;
+ size_t len;
+
+ if (virtualPath == NULL || realPath == NULL)
+ {
+ rc = ESR_INVALID_ARGUMENT;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+
+ len = LSTRLEN(virtualPath) + 1;
+ if (virtualPath[LSTRLEN(virtualPath)-1] != L('/'))
+ ++len;
+ key = MALLOC(sizeof(LCHAR) * len, MTAG);
+ if (key == NULL)
+ {
+ rc = ESR_OUT_OF_MEMORY;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ LSTRCPY(key, virtualPath);
+ /* Make sure paths end with '/' */
+ CHKLOG(rc, PFileSystemCanonicalSlashes(key));
+ if (key[LSTRLEN(key)-1] != L('/'))
+ LSTRCAT(key, L("/"));
+ value = MALLOC(sizeof(LCHAR) * (LSTRLEN(realPath) + 1), MTAG);
+ if (value == NULL)
+ {
+ rc = ESR_OUT_OF_MEMORY;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ LSTRCPY(value, realPath);
+
+ /* Make sure realPath is not an empty string */
+ lstrtrim(value);
+ if (LSTRLEN(value) == 0)
+ {
+ FREE(value);
+ value = NULL;
+ rc = ESR_INVALID_ARGUMENT;
+ PLogError(L("%s: realPath cannot be empty"), ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+
+ /* Make sure paths end with '/' */
+ CHKLOG(rc, PFileSystemCanonicalSlashes(value));
+ if (value[LSTRLEN(value)-1] != L('/'))
+ LSTRCAT(value, L("/"));
+
+ CHKLOG(rc, PHashTableContainsKey(impl->directoryMap, key, &exists));
+ if (exists)
+ {
+ LCHAR* oldValue;
+
+ CHKLOG(rc, PHashTableGetValue(impl->directoryMap, key, (void **)&oldValue));
+ if (LSTRCMP(oldValue, value) != 0)
+ {
+ rc = ESR_IDENTIFIER_COLLISION;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ }
+ CHKLOG(rc, PHashTablePutValue(impl->directoryMap, key, value, NULL));
+ CHKLOG(rc, PHashTablePutValue(PFileSystemPathMap, key, self, NULL));
+ return ESR_SUCCESS;
+CLEANUP:
+ FREE(key);
+ FREE(value);
+ return rc;
+}
+
+ESR_ReturnCode PANSIFileSystemRemovePathImpl(PFileSystem* self, const LCHAR* virtualPath)
+{
+ PANSIFileSystemImpl* impl = (PANSIFileSystemImpl*) self;
+ LCHAR path[P_PATH_MAX];
+ LCHAR* key;
+ LCHAR* value;
+ PHashTableEntry* entry;
+ ESR_ReturnCode rc;
+
+ if (virtualPath == NULL)
+ {
+ rc = ESR_INVALID_ARGUMENT;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ /* Make sure paths end with '/' */
+ LSTRCPY(path, virtualPath);
+ CHKLOG(rc, PFileSystemCanonicalSlashes(path));
+ if (path[LSTRLEN(path)-1] != L('/'))
+ LSTRCAT(path, L("/"));
+ CHKLOG(rc, PHashTableGetEntry(impl->directoryMap, path, &entry));
+ CHKLOG(rc, PHashTableEntryGetKeyValue(entry, (void **)&key, (void **)&value));
+ CHKLOG(rc, PHashTableEntryRemove(entry));
+ CHKLOG(rc, PHashTableRemoveValue(PFileSystemPathMap, key, NULL));
+ FREE(key);
+ FREE(value);
+ return ESR_SUCCESS;
+CLEANUP:
+ return rc;
+}
+
+ESR_ReturnCode PANSIFileSystemGetRealPathImpl(PFileSystem* self, LCHAR* path, size_t* len)
+{
+ PANSIFileSystemImpl* impl = (PANSIFileSystemImpl*) self;
+ PHashTableEntry* entry;
+ LCHAR* key;
+ LCHAR* value;
+ LCHAR* bestKey = NULL;
+ LCHAR* bestValue = NULL;
+ ESR_BOOL isAbsolute;
+ ESR_ReturnCode rc;
+
+ CHKLOG(rc, PFileSystemGetAbsolutePath(path, len));
+ CHKLOG(rc, PHashTableEntryGetFirst(impl->directoryMap, &entry));
+ while (entry != NULL)
+ {
+ CHKLOG(rc, PHashTableEntryGetKeyValue(entry, (void**)&key, (void**)&value));
+ if (LSTRNCMP(path, key, LSTRLEN(key)) == 0)
+ {
+ /* File-system handles file path */
+ if (bestKey == NULL || LSTRLEN(key) > LSTRLEN(bestKey))
+ {
+ /* Found a better match -- the new key is a subdirectory of the previous bestKey */
+ bestKey = key;
+ bestValue = value;
+ }
+ }
+ CHKLOG(rc, PHashTableEntryAdvance(&entry));
+ }
+ if (bestKey == NULL)
+ {
+ rc = ESR_INVALID_STATE;
+ PLogError(L("PANSIFileSystem does not handle the specified path: %s"), path);
+ goto CLEANUP;
+ }
+
+ if (LSTRLEN(bestValue) + 1 > *len)
+ {
+ *len = LSTRLEN(bestValue) + 1;
+ rc = ESR_BUFFER_OVERFLOW;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ /* Delete the virtual-path */
+ LSTRCPY(path, path + LSTRLEN(bestKey));
+
+ CHKLOG(rc, PFileSystemIsAbsolutePath(path, &isAbsolute));
+ if (LSTRCMP(bestValue, L("/")) == 0 && isAbsolute)
+ {
+ /* do nothing */
+ }
+ else
+ {
+ /* Insert the key-path */
+ CHKLOG(rc, lstrinsert(bestValue, path, 0, len));
+ }
+ return ESR_SUCCESS;
+CLEANUP:
+ return rc;
+}
+
+ESR_ReturnCode PANSIFileSystemCreatePFileImpl(PFileSystem* self, const LCHAR* path, ESR_BOOL littleEndian, PFile** file)
+{
+ LCHAR realPath[P_PATH_MAX];
+ size_t len;
+ ESR_ReturnCode rc;
+
+ LSTRCPY(realPath, path);
+ len = P_PATH_MAX;
+ CHKLOG(rc, PANSIFileSystemGetRealPathImpl(self, realPath, &len));
+ return PANSIFileCreateImpl(realPath, littleEndian, file);
+CLEANUP:
+ return rc;
+}
+
+ESR_ReturnCode PANSIFileSystemSetDefault(ESR_BOOL isDefault)
+{
+ PANSIFileSystemImpl* impl = (PANSIFileSystemImpl*) PANSIFileSystemSingleton;
+ ESR_BOOL exists;
+ LCHAR* key = NULL;
+ LCHAR* value = NULL;
+ PHashTableEntry* entry;
+ ESR_ReturnCode rc;
+
+ if (isDefault)
+ {
+
+ key = MALLOC(sizeof(LCHAR), MTAG);
+ if (key == NULL)
+ {
+ rc = ESR_OUT_OF_MEMORY;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ LSTRCPY(key, L(""));
+ value = MALLOC(sizeof(LCHAR), MTAG);
+ if (value == NULL)
+ {
+ rc = ESR_OUT_OF_MEMORY;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ LSTRCPY(value, L(""));
+
+ CHKLOG(rc, PHashTableContainsKey(impl->directoryMap, key, &exists));
+ if (exists)
+ {
+ LCHAR* key;
+ LCHAR* value;
+
+ CHKLOG(rc, PHashTableGetEntry(impl->directoryMap, L(""), &entry));
+ CHKLOG(rc, PHashTableEntryGetKeyValue(entry, (void **)&key, (void **)&value));
+ CHKLOG(rc, PHashTableEntryRemove(entry));
+ CHKLOG(rc, PHashTableRemoveValue(PFileSystemPathMap, key, NULL));
+ FREE(key);
+ FREE(value);
+ }
+ CHKLOG(rc, PHashTablePutValue(impl->directoryMap, key, value, NULL));
+ CHKLOG(rc, PHashTablePutValue(PFileSystemPathMap, key, PANSIFileSystemSingleton, NULL));
+
+ /* Set virtual current working directory to native current working directory */
+ }
+ else
+ {
+ CHKLOG(rc, PHashTableContainsKey(impl->directoryMap, L(""), &exists));
+ if (exists)
+ {
+ LCHAR* key;
+ LCHAR* value;
+
+ CHKLOG(rc, PHashTableGetEntry(impl->directoryMap, L(""), &entry));
+ CHKLOG(rc, PHashTableEntryGetKeyValue(entry, (void **)&key, (void **)&value));
+
+ CHKLOG(rc, PHashTableContainsKey(PFileSystemPathMap, L(""), &exists));
+ if (exists)
+ {
+ LCHAR* key;
+ PFileSystem* value;
+ PHashTableEntry* entry;
+
+ CHKLOG(rc, PHashTableGetEntry(PFileSystemPathMap, L(""), &entry));
+ CHKLOG(rc, PHashTableEntryGetKeyValue(entry, (void **)&key, (void **)&value));
+ if (value == PANSIFileSystemSingleton)
+ CHKLOG(rc, PHashTableEntryRemove(entry));
+ }
+
+ CHKLOG(rc, PHashTableEntryRemove(entry));
+ FREE(key);
+ FREE(value);
+ }
+ }
+ return ESR_SUCCESS;
+CLEANUP:
+ FREE(key);
+ FREE(value);
+ return rc;
+}
diff --git a/portable/src/PFile.c b/portable/src/PFile.c
new file mode 100644
index 0000000..9320846
--- /dev/null
+++ b/portable/src/PFile.c
@@ -0,0 +1,521 @@
+/*---------------------------------------------------------------------------*
+ * PFile.c *
+ * *
+ * Copyright 2007, 2008 Nuance Communciations, Inc. *
+ * *
+ * 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 "LCHAR.h"
+#include "pendian.h"
+#include "PFile.h"
+#include "PFileSystem.h"
+#include "plog.h"
+#include "pstdio.h"
+
+
+ESR_ReturnCode PFileDestroy(PFile* self)
+{
+ if (self == NULL)
+ {
+ PLogError(L("ESR_INVALID_ARGUMENT"));
+ return ESR_INVALID_ARGUMENT;
+ }
+ return self->destroy(self);
+}
+
+ESR_ReturnCode PFileOpen(PFile* self, const LCHAR* mode)
+{
+ if (self == NULL)
+ {
+ PLogError(L("ESR_INVALID_ARGUMENT"));
+ return ESR_INVALID_ARGUMENT;
+ }
+ return self->open(self, mode);
+}
+
+ESR_ReturnCode PFileClose(PFile* self)
+{
+ if (self == NULL)
+ {
+ PLogError(L("ESR_INVALID_ARGUMENT"));
+ return ESR_INVALID_ARGUMENT;
+ }
+ return self->close(self);
+}
+
+ESR_ReturnCode PFileRead(PFile* self, void* buffer, size_t size, size_t* count)
+{
+ if (self == NULL)
+ {
+ PLogError(L("ESR_INVALID_ARGUMENT"));
+ return ESR_INVALID_ARGUMENT;
+ }
+ return self->read(self, buffer, size, count);
+}
+
+ESR_ReturnCode PFileWrite(PFile* self, void* buffer, size_t size, size_t* count)
+{
+ if (self == NULL)
+ {
+ PLogError(L("ESR_INVALID_ARGUMENT"));
+ return ESR_INVALID_ARGUMENT;
+ }
+ return self->write(self, buffer, size, count);
+}
+
+ESR_ReturnCode PFileFlush(PFile* self)
+{
+ if (self == NULL)
+ {
+ PLogError(L("ESR_INVALID_ARGUMENT"));
+ return ESR_INVALID_ARGUMENT;
+ }
+ return self->flush(self);
+}
+
+ESR_ReturnCode PFileSeek(PFile* self, long offset, int origin)
+{
+ if (self == NULL)
+ {
+ PLogError(L("ESR_INVALID_ARGUMENT"));
+ return ESR_INVALID_ARGUMENT;
+ }
+ return self->seek(self, offset, origin);
+}
+
+
+ESR_ReturnCode PFileGetPosition(PFile* self, size_t* position)
+{
+ if (self == NULL)
+ {
+ PLogError(L("ESR_INVALID_ARGUMENT"));
+ return ESR_INVALID_ARGUMENT;
+ }
+ return self->getPosition(self, position);
+}
+
+ESR_ReturnCode PFileIsOpen(PFile* self, ESR_BOOL* isOpen)
+{
+ if (self == NULL)
+ {
+ PLogError(L("ESR_INVALID_ARGUMENT"));
+ return ESR_INVALID_ARGUMENT;
+ }
+ return self->isOpen(self, isOpen);
+}
+
+ESR_ReturnCode PFileIsEOF(PFile* self, ESR_BOOL* isEof)
+{
+ if (self == NULL)
+ {
+ PLogError(L("ESR_INVALID_ARGUMENT"));
+ return ESR_INVALID_ARGUMENT;
+ }
+ return self->isEOF(self, isEof);
+}
+
+ESR_ReturnCode PFileGetFilename(PFile* self, LCHAR* filename, size_t* len)
+{
+ ESR_ReturnCode rc;
+
+ if (self == NULL)
+ {
+ PLogError(L("ESR_INVALID_ARGUMENT"));
+ return ESR_INVALID_ARGUMENT;
+ }
+ rc = self->getFilename(self, filename, len);
+ return rc;
+}
+
+ESR_ReturnCode PFileIsErrorSet(PFile* self, ESR_BOOL* isError)
+{
+ if (self == NULL)
+ {
+ PLogError(L("ESR_INVALID_ARGUMENT"));
+ return ESR_INVALID_ARGUMENT;
+ }
+ return self->isErrorSet(self, isError);
+}
+
+ESR_ReturnCode PFileClearError(PFile* self)
+{
+ if (self == NULL)
+ {
+ PLogError(L("ESR_INVALID_ARGUMENT"));
+ return ESR_INVALID_ARGUMENT;
+ }
+ return self->clearError(self);
+}
+
+ESR_ReturnCode PFileVfprintf(PFile* self, int* result, const LCHAR* format, va_list args)
+{
+ ESR_ReturnCode rc;
+
+ if (self == NULL)
+ {
+ PLogError(L("ESR_INVALID_ARGUMENT"));
+ return ESR_INVALID_ARGUMENT;
+ }
+ rc = self->vfprintf(self, result, format, args);
+ return rc;
+}
+
+ESR_ReturnCode PFileFgetc(PFile* self, LINT* result)
+{
+ if (self == NULL)
+ {
+ PLogError(L("ESR_INVALID_ARGUMENT"));
+ return ESR_INVALID_ARGUMENT;
+ }
+ return self->fgetc(self, result);
+}
+
+ESR_ReturnCode PFileFgets(PFile* self, LCHAR* string, int n, LCHAR** result)
+{
+ if (self == NULL)
+ {
+ PLogError(L("ESR_INVALID_ARGUMENT"));
+ return ESR_INVALID_ARGUMENT;
+ }
+ return self->fgets(self, string, n, result);
+}
+
+ESR_ReturnCode PFileReadInt(PFile* self, int* value)
+{
+ LCHAR number[MAX_INT_DIGITS+1];
+ size_t i, bufferSize, count, totalRead = 0;
+ ESR_ReturnCode rc;
+
+ /* Skip whitespace before token */
+ do
+ {
+ count = pfread(number, sizeof(LCHAR), MAX_INT_DIGITS, self);
+ totalRead += count;
+ if (count < MAX_INT_DIGITS)
+ {
+ if (pferror(self))
+ {
+ rc = ESR_READ_ERROR;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ else
+ {
+ rc = ESR_INVALID_STATE;
+ PLogError(L("%s: reached end of file before finding token"), ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ }
+ /* locate first non-whitespace character */
+ for (i = 0; i < count && LISSPACE(number[i]); ++i);
+ }
+ while (i == count);
+ bufferSize = count - i;
+
+ /* Fill remainder of buffer */
+ if (bufferSize < MAX_INT_DIGITS)
+ {
+ count = pfread(number + bufferSize, sizeof(LCHAR), MAX_INT_DIGITS - bufferSize, self);
+ bufferSize += count;
+ totalRead += count;
+ if (count < MAX_INT_DIGITS - bufferSize && pferror(self))
+ {
+ rc = ESR_READ_ERROR;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ }
+
+ /* locate first whitespace character */
+ for (i = 0; i < bufferSize && !LISSPACE(number[i]); ++i);
+ if (i < bufferSize)
+ {
+ /* unread anything after the token */
+ if (PFileSeek(self, - (int)(bufferSize - i), SEEK_CUR))
+ {
+ rc = ESR_SEEK_ERROR;
+ PLogError(ESR_rc2str(rc));
+ }
+ totalRead -= bufferSize - i;
+ number[i] = L('\0');
+ }
+
+ if (number[0] != L('-') && !LISDIGIT(number[0]))
+ {
+ rc = ESR_INVALID_STATE;
+ PLogError(L("%s: token was not number (%s)"), ESR_rc2str(rc), number);
+ goto CLEANUP;
+ }
+
+ CHKLOG(rc, lstrtoi(number, value, 10));
+ return rc;
+CLEANUP:
+ if (PFileSeek(self, - (int) count, SEEK_CUR))
+ PLogError(L("ESR_SEEK_ERROR"));
+ return rc;
+}
+
+ESR_ReturnCode PFileReadLCHAR(PFile* self, LCHAR* value, size_t len)
+{
+ size_t i, bufferSize, count, totalRead = 0;
+ ESR_ReturnCode rc = ESR_SUCCESS;
+
+ /* Skip whitespace before token */
+ do
+ {
+ count = pfread(value, sizeof(LCHAR), len, self);
+ totalRead += count;
+ if (count < len)
+ {
+ if (pferror(self))
+ {
+ rc = ESR_READ_ERROR;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ else
+ {
+ rc = ESR_INVALID_STATE;
+ PLogError(L("%s: reached end of file before finding token"), ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ }
+ /* locate first non-whitespace character */
+ for (i = 0; i < count && LISSPACE(value[i]); ++i);
+ }
+ while (i == count);
+ bufferSize = count - i;
+
+ /* Fill remainder of buffer */
+ if (bufferSize < len)
+ {
+ count = pfread(value + bufferSize, sizeof(LCHAR), len - bufferSize, self);
+ bufferSize += count;
+ totalRead += count;
+ if (count < len - bufferSize && pferror(self))
+ {
+ rc = ESR_READ_ERROR;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ }
+
+ /* locate first whitespace character */
+ for (i = 0; i < bufferSize && !LISSPACE(value[i]); ++i);
+ if (i < bufferSize)
+ {
+ /* unread anything after the token */
+ if (PFileSeek(self, -(int)(bufferSize - i), SEEK_CUR))
+ {
+ rc = ESR_SEEK_ERROR;
+ PLogError(ESR_rc2str(rc));
+ }
+ totalRead -= bufferSize - i;
+ value[i] = L('\0');
+ }
+ return rc;
+CLEANUP:
+ if (PFileSeek(self, - (int) count, SEEK_CUR))
+ PLogError(L("ESR_SEEK_ERROR"));
+ return rc;
+}
+
+PFile* pfopen(const LCHAR* filename, const LCHAR* mode)
+{
+ PFile* result;
+ ESR_ReturnCode rc;
+ ESR_BOOL isLittleEndian;
+
+#if __BYTE_ORDER==__LITTLE_ENDIAN
+ isLittleEndian = ESR_TRUE;
+#else
+ isLittleEndian = ESR_FALSE;
+#endif
+
+ rc = PFileSystemCreatePFile(filename, isLittleEndian, &result);
+ if (rc != ESR_SUCCESS)
+ return NULL;
+ rc = result->open(result, mode);
+ if (rc != ESR_SUCCESS)
+ {
+ result->destroy(result);
+ return NULL;
+ }
+ return result;
+}
+
+size_t pfread(void* buffer, size_t size, size_t count, PFile* stream)
+{
+ ESR_ReturnCode rc;
+
+ rc = PFileRead(stream, buffer, size, &count);
+ if (rc != ESR_SUCCESS)
+ return 0;
+ return count;
+}
+
+size_t pfwrite(const void* buffer, size_t size, size_t count, PFile* stream)
+{
+ ESR_ReturnCode rc;
+
+ rc = PFileWrite(stream, buffer, size, &count);
+ if (rc != ESR_SUCCESS)
+ return 0;
+ return count;
+}
+
+int pfclose(PFile* stream)
+{
+ ESR_ReturnCode rc;
+
+ rc = PFileDestroy(stream);
+ if (rc != ESR_SUCCESS)
+ return PEOF;
+ return 0;
+}
+
+void prewind(PFile* stream)
+{
+ PFileSeek(stream, 0, SEEK_SET);
+}
+
+int pfseek(PFile* stream, long offset, int origin)
+{
+ ESR_ReturnCode rc;
+
+ rc = PFileSeek(stream, offset, origin);
+ if (rc != ESR_SUCCESS)
+ return 1;
+ return 0;
+}
+
+long pftell(PFile* stream)
+{
+ size_t result;
+ ESR_ReturnCode rc;
+
+ rc = PFileGetPosition(stream, &result);
+ if (rc != ESR_SUCCESS)
+ return -1;
+ return result;
+}
+
+int pfeof(PFile* stream)
+{
+ ESR_BOOL eof;
+
+ PFileIsEOF(stream, &eof);
+ if (!eof)
+ return 0;
+ return 1;
+}
+
+int pferror(PFile* stream)
+{
+ ESR_BOOL error;
+
+ PFileIsErrorSet(stream, &error);
+ if (!error)
+ return 0;
+ return 1;
+}
+
+void pclearerr(PFile* stream)
+{
+ PFileClearError(stream);
+}
+
+int pfflush(PFile* stream)
+{
+ ESR_ReturnCode rc;
+
+ rc = PFileFlush(stream);
+ if (rc != ESR_SUCCESS)
+ return PEOF;
+ return 0;
+}
+
+LCHAR* pfgets(LCHAR* string, int n, PFile* self)
+{
+ LCHAR* result;
+ ESR_ReturnCode rc;
+
+ rc = PFileFgets(self, string, n, &result);
+ if (rc != ESR_SUCCESS)
+ return NULL;
+ return result;
+}
+
+LINT pfgetc(PFile* self)
+{
+ LINT result;
+ ESR_ReturnCode rc;
+
+ rc = PFileFgetc(self, &result);
+ if (rc != ESR_SUCCESS)
+ return PEOF;
+ return result;
+}
+
+int pfprintf(PFile* stream, const LCHAR* format, ...)
+{
+#ifdef FINAL_RELEASE
+ return 0;
+#else
+ va_list args;
+ int result;
+ ESR_ReturnCode rc;
+
+ va_start(args, format);
+ rc = PFileVfprintf(stream, &result, format, args);
+ va_end(args);
+ if (rc != ESR_SUCCESS)
+ return -1;
+ return result;
+#endif
+}
+
+int pvfprintf(PFile* stream, const LCHAR* format, va_list argptr)
+{
+#ifdef FINAL_RELEASE
+ return 0;
+#else
+ int result;
+ ESR_ReturnCode rc;
+
+ rc = PFileVfprintf(stream, &result, format, argptr);
+ if (rc != ESR_SUCCESS)
+ return -1;
+ return result;
+#endif
+}
+
+int pprintf(const LCHAR* format, ...)
+{
+#ifdef FINAL_RELEASE
+ return 0;
+#else
+ va_list args;
+ int result;
+ ESR_ReturnCode rc;
+
+ va_start(args, format);
+ rc = PFileVfprintf(PSTDOUT, &result, format, args);
+ va_end(args);
+ if (rc != ESR_SUCCESS)
+ return -1;
+ return result;
+#endif
+}
diff --git a/portable/src/PFileImpl.c b/portable/src/PFileImpl.c
new file mode 100644
index 0000000..72abfba
--- /dev/null
+++ b/portable/src/PFileImpl.c
@@ -0,0 +1,199 @@
+/*---------------------------------------------------------------------------*
+ * PFileImpl.c *
+ * *
+ * Copyright 2007, 2008 Nuance Communciations, Inc. *
+ * *
+ * 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 "passert.h"
+#include "pendian.h"
+#include "PFileImpl.h"
+#include "PFileSystem.h"
+#include "plog.h"
+#include "pmemory.h"
+#include "pstdio.h"
+#include "ptypes.h"
+
+#define MTAG NULL
+
+
+/**
+ * Initializes variables declared in the superinterface.
+ */
+ESR_ReturnCode PFileCreateImpl(PFile* self, const LCHAR* filename, ESR_BOOL isLittleEndian)
+{
+ PFileImpl* impl = (PFileImpl*) self;
+ ESR_ReturnCode rc;
+#ifdef USE_THREAD
+ ESR_BOOL threadingEnabled;
+#endif
+
+#ifdef USE_THREAD
+ impl->lock = NULL;
+#endif
+ impl->littleEndian = isLittleEndian;
+
+ impl->Interface.destroy = &PFileDestroyImpl;
+ impl->Interface.getFilename = &PFileGetFilenameImpl;
+ impl->Interface.vfprintf = &PFileVfprintfImpl;
+ impl->filename = MALLOC(sizeof(LCHAR) * (LSTRLEN(filename) + 1), MTAG);
+
+ if (impl->filename == NULL)
+ {
+ rc = ESR_OUT_OF_MEMORY;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ LSTRCPY(impl->filename, filename);
+
+#ifdef USE_THREAD
+ rc = PtrdIsEnabled(&threadingEnabled);
+ if (rc != ESR_SUCCESS)
+ {
+ pfprintf(PSTDERR, L("[%s:%d] PtrdIsEnabled failed with %s\n"), __FILE__, __LINE__, ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ if (threadingEnabled)
+ {
+ rc = PtrdMonitorCreate(&impl->lock);
+ if (rc != ESR_SUCCESS)
+ goto CLEANUP;
+ }
+#endif
+ return ESR_SUCCESS;
+CLEANUP:
+ self->destroy(self);
+ return rc;
+}
+
+
+#ifdef USE_THREAD
+#define LOCK_MUTEX(rc, impl) \
+ if (impl->lock != NULL) \
+ CHKLOG(rc, PtrdMonitorLock(impl->lock));
+#else
+#define LOCK_MUTEX(rc, impl)
+#endif
+
+
+#ifdef USE_THREAD
+#define CLEANUP_AND_RETURN(rc, impl) \
+ if (impl->lock!=NULL) \
+ CHKLOG(rc, PtrdMonitorUnlock(impl->lock)); \
+ return ESR_SUCCESS; \
+ CLEANUP: \
+ if (impl->lock!=NULL) \
+ PtrdMonitorUnlock(impl->lock); \
+ return rc;
+#else
+#define CLEANUP_AND_RETURN(rc, impl) \
+ return ESR_SUCCESS; \
+ CLEANUP: \
+ return rc;
+#endif
+
+
+ESR_ReturnCode PFileDestroyImpl(PFile* self)
+{
+ PFileImpl* impl = (PFileImpl*) self;
+ ESR_ReturnCode rc;
+ ESR_BOOL isOpen;
+
+ LOCK_MUTEX(rc, impl);
+ CHKLOG(rc, self->isOpen(self, &isOpen));
+ if (isOpen)
+ CHKLOG(rc, self->close(self));
+ if (impl->filename)
+ {
+ FREE(impl->filename);
+ impl->filename = NULL;
+ }
+#ifdef USE_THREAD
+ if (impl->lock != NULL)
+ {
+ PtrdMonitorUnlock(impl->lock);
+ rc = PtrdMonitorDestroy(impl->lock);
+ if (rc != ESR_SUCCESS)
+ goto CLEANUP;
+ }
+#endif
+ return ESR_SUCCESS;
+CLEANUP:
+#ifdef USE_THREAD
+ if (impl->lock != NULL)
+ PtrdMonitorUnlock(impl->lock);
+#endif
+ return rc;
+}
+
+ESR_ReturnCode PFileGetFilenameImpl(PFile* self, LCHAR* filename, size_t* len)
+{
+ PFileImpl* impl = (PFileImpl*) self;
+ ESR_ReturnCode rc;
+
+ if (self == NULL || len == NULL)
+ {
+ PLogError(L("ESR_INVALID_ARGUMENT"));
+ return ESR_INVALID_ARGUMENT;
+ }
+ LOCK_MUTEX(rc, impl);
+ if (LSTRLEN(impl->filename) + 1 > *len)
+ {
+ *len = LSTRLEN(impl->filename) + 1;
+ rc = ESR_BUFFER_OVERFLOW;
+ goto CLEANUP;
+ }
+ LSTRCPY(filename, impl->filename);
+ CLEANUP_AND_RETURN(rc, impl);
+}
+
+ESR_ReturnCode PFileVfprintfImpl(PFile* self, int* result, const LCHAR* format, va_list args)
+{
+ ESR_ReturnCode rc;
+ ESR_BOOL isOpen;
+#define BUFFER_SIZE 5120
+ static LCHAR buffer[BUFFER_SIZE];
+ size_t len;
+
+ if (self == NULL)
+ {
+ PLogError(L("ESR_INVALID_ARGUMENT"));
+ return ESR_INVALID_ARGUMENT;
+ }
+
+ CHKLOG(rc, self->isOpen(self, &isOpen));
+ if (!isOpen)
+ {
+ rc = ESR_OPEN_ERROR;
+ PLogError(L("%s: cannot operate on closed file"), ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+
+ /*
+ * fprintf() is computationally expensive, so we compute its output without grabbing a lock
+ * and only lock while actually writing the results into the file.
+ */
+ if (result != NULL)
+ *result = vsprintf(buffer, format, args);
+ else
+ vsprintf(buffer, format, args);
+ len = LSTRLEN(buffer);
+ passert(len < BUFFER_SIZE);
+
+ CHKLOG(rc, self->write(self, buffer, sizeof(LCHAR), &len));
+ return ESR_SUCCESS;
+CLEANUP:
+ return rc;
+}
diff --git a/portable/src/PFileSystem.c b/portable/src/PFileSystem.c
new file mode 100644
index 0000000..14fbf6b
--- /dev/null
+++ b/portable/src/PFileSystem.c
@@ -0,0 +1,552 @@
+/*---------------------------------------------------------------------------*
+ * PFileSystem.c *
+ * *
+ * Copyright 2007, 2008 Nuance Communciations, Inc. *
+ * *
+ * 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 "ArrayList.h"
+#include "LCHAR.h"
+#include "PFileSystem.h"
+#include "PFileSystemImpl.h"
+#include "phashtable.h"
+#include "plog.h"
+#include "pmemory.h"
+
+
+#define MTAG NULL
+
+/**
+ * Indicates if PFileSystem is initialized.
+ */
+extern ESR_BOOL PFileSystemCreated;
+
+/**
+ * [file path, PFileSystem*] mapping.
+ */
+extern PHashTable* PFileSystemPathMap;
+
+/**
+ * Current working directory.
+ */
+extern LCHAR PFileSystemCurrentDirectory[P_PATH_MAX];
+
+PORTABLE_API ESR_ReturnCode PFileSystemCanonicalSlashes(LCHAR* path)
+{
+ ESR_ReturnCode rc;
+
+ if (path == NULL)
+ {
+ rc = ESR_INVALID_ARGUMENT;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+
+ lstrtrim(path);
+ CHKLOG(rc, lstrreplace(path, L('\\'), L('/')));
+ return ESR_SUCCESS;
+CLEANUP:
+ return rc;
+}
+
+ESR_ReturnCode PFileSystemLinearToPathTokens(const LCHAR* path, LCHAR*** tokenArray, size_t* count)
+{
+ ESR_ReturnCode rc;
+ const LCHAR* beginning;
+ const LCHAR* ending;
+ LCHAR linear[P_PATH_MAX];
+ ArrayList* arrayList = NULL;
+ LCHAR* value = NULL;
+ size_t i;
+
+ if (path == NULL || tokenArray == NULL || count == NULL)
+ {
+ rc = ESR_INVALID_ARGUMENT;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ LSTRCPY(linear, path);
+ CHKLOG(rc, PFileSystemCanonicalSlashes(linear));
+ CHKLOG(rc, ArrayListCreate(&arrayList));
+ beginning = linear;
+ while (ESR_TRUE)
+ {
+ ending = LSTRCHR(beginning, L('/'));
+ if (ending == NULL)
+ ending = beginning + LSTRLEN(beginning);
+ value = MALLOC(sizeof(LCHAR) * (ending - beginning + 1 + 1), MTAG);
+ if (value == NULL)
+ {
+ rc = ESR_OUT_OF_MEMORY;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ LSTRNCPY(value, beginning, ending - beginning + 1);
+ value[ending-beginning+1] = L('\0');
+ CHKLOG(rc, lstrtrim(value));
+ if (LSTRLEN(value) == 0)
+ {
+ FREE(value);
+ value = NULL;
+ }
+ else
+ {
+ CHKLOG(rc, arrayList->add(arrayList, value));
+ value = NULL;
+ }
+ if (*ending == 0)
+ break;
+ beginning = ending + 1;
+ }
+
+ /* Build static token array */
+ CHKLOG(rc, arrayList->getSize(arrayList, count));
+ *tokenArray = MALLOC(*count * sizeof(LCHAR*), MTAG);
+ if (*tokenArray == NULL)
+ {
+ rc = ESR_OUT_OF_MEMORY;
+ goto CLEANUP;
+ }
+ for (i = 0; i < *count; ++i)
+ {
+ rc = arrayList->get(arrayList, i, (void**)(&(*tokenArray)[i]));
+ if (rc != ESR_SUCCESS)
+ goto CLEANUP;
+ }
+ rc = arrayList->destroy(arrayList);
+ if (rc != ESR_SUCCESS)
+ goto CLEANUP;
+ return ESR_SUCCESS;
+CLEANUP:
+ FREE(value);
+ if (arrayList != NULL)
+ {
+ ESR_ReturnCode cleanRC;
+
+ cleanRC = arrayList->getSize(arrayList, count);
+ if (cleanRC != ESR_SUCCESS)
+ return rc;
+ for (i = 0; i < *count; ++i)
+ {
+ cleanRC = arrayList->get(arrayList, 0, (void**)&value);
+ if (cleanRC != ESR_SUCCESS)
+ return rc;
+ FREE(value);
+ cleanRC = arrayList->remove(arrayList, 0);
+ if (cleanRC != ESR_SUCCESS)
+ return rc;
+ }
+ arrayList->destroy(arrayList);
+ }
+ return rc;
+}
+
+ESR_ReturnCode PFileSystemIsAbsolutePath(const LCHAR* path, ESR_BOOL* isAbsolute)
+{
+ LCHAR canonical[P_PATH_MAX];
+ ESR_ReturnCode rc;
+
+ if (isAbsolute == NULL)
+ {
+ rc = ESR_INVALID_ARGUMENT;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ LSTRCPY(canonical, path);
+ CHKLOG(rc, PFileSystemCanonicalSlashes(canonical));
+
+ *isAbsolute = (canonical[0] == '/' ||
+ (LISALPHA(canonical[0]) && canonical[1] == ':' && canonical[2] == '/'));
+ return ESR_SUCCESS;
+CLEANUP:
+ return rc;
+}
+
+ESR_ReturnCode PFileSystemGetAbsolutePath(LCHAR* path, size_t* len)
+{
+ ESR_ReturnCode rc;
+#define MAX_PATH_TOKENS 20
+ LCHAR** tokens = NULL;
+ size_t tokenLen = 0, i;
+ ESR_BOOL isAbsolute;
+
+ if (path == NULL || len == NULL)
+ {
+ rc = ESR_INVALID_ARGUMENT;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ CHKLOG(rc, PFileSystemIsAbsolutePath(path, &isAbsolute));
+
+ /* Prefix relative paths with the current working directory */
+ if (!isAbsolute)
+ {
+ LCHAR cwd[P_PATH_MAX];
+ size_t len2;
+
+ len2 = P_PATH_MAX;
+ CHKLOG(rc, PFileSystemGetcwd(cwd, &len2));
+ len2 = *len;
+ CHKLOG(rc, lstrinsert(cwd, path, 0, &len2));
+ }
+
+ CHKLOG(rc, PFileSystemCanonicalSlashes(path));
+ tokenLen = MAX_PATH_TOKENS;
+ CHKLOG(rc, PFileSystemLinearToPathTokens(path, &tokens, &tokenLen));
+
+ LSTRCPY(path, L(""));
+ for (i = 0; i < tokenLen; ++i)
+ {
+ if (LSTRCMP(tokens[i], L("../")) == 0)
+ {
+ size_t len2;
+
+ len2 = *len;
+ passert(path[LSTRLEN(path)-1] == L('/'));
+ CHKLOG(rc, PFileSystemGetParentDirectory(path, &len2));
+ }
+ else if (LSTRCMP(tokens[i], L("./")) == 0)
+ {
+ if (i > 0)
+ {
+ /* do nothing */
+ }
+ else
+ {
+ LSTRCPY(path, L("./"));
+ }
+ }
+ else
+ LSTRCAT(path, tokens[i]);
+ FREE(tokens[i]);
+ tokens[i] = NULL;
+ }
+ FREE(tokens);
+ return ESR_SUCCESS;
+CLEANUP:
+ if (tokens != NULL)
+ {
+ for (i = 0; i < tokenLen; ++i)
+ {
+ FREE(tokens[i]);
+ tokens[i] = NULL;
+ }
+ }
+ return rc;
+}
+
+ESR_ReturnCode PFileSystemIsCreated(ESR_BOOL* isCreated)
+{
+ ESR_ReturnCode rc;
+
+ if (isCreated == NULL)
+ {
+ rc = ESR_INVALID_ARGUMENT;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ *isCreated = PFileSystemCreated;
+ return ESR_SUCCESS;
+CLEANUP:
+ return rc;
+}
+
+/**
+ * Given a path, returns the associated file-system and relative path.
+ *
+ * @param path Path to look up
+ * @param fs [out] File-system which matches the path
+ * @param relativePath [out] Relative path associated with match (should have length P_PATH_MAX)
+ */
+ESR_ReturnCode PFileSystemGetFS(const LCHAR* path, PFileSystem** fileSystem, LCHAR* relativePath)
+{
+ ESR_ReturnCode rc;
+ PHashTableEntry* entry;
+ LCHAR* key;
+ PFileSystem* value;
+ LCHAR* bestKey = NULL;
+ PFileSystem* bestValue = NULL;
+
+ CHKLOG(rc, PHashTableEntryGetFirst(PFileSystemPathMap, &entry));
+ while (entry != NULL)
+ {
+ CHKLOG(rc, PHashTableEntryGetKeyValue(entry, (void **)&key, (void **)&value));
+ if (LSTRSTR(path, key) == path)
+ {
+ /* File-system handles file path */
+
+ if (bestKey == NULL || LSTRLEN(key) > LSTRLEN(bestKey))
+ {
+ /* Found a better match -- the new key is a subdirectory of the previous bestKey */
+ bestKey = key;
+ bestValue = value;
+ }
+ }
+ CHKLOG(rc, PHashTableEntryAdvance(&entry));
+ }
+ if (bestKey == NULL)
+ {
+ rc = ESR_INVALID_STATE;
+ PLogError(L("No file-system handles the specified path (%s)"), path);
+ goto CLEANUP;
+ }
+ *fileSystem = bestValue;
+ if (relativePath != NULL)
+ {
+ ESR_BOOL isAbsolute;
+
+ CHKLOG(rc, PFileSystemIsAbsolutePath(path + LSTRLEN(bestKey), &isAbsolute));
+ LSTRCPY(relativePath, L(""));
+ if (!isAbsolute)
+ {
+ /* Make sure that the relative path is relative to the root of the file-system */
+ LSTRCAT(relativePath, L("/"));
+ }
+ LSTRCAT(relativePath, path + LSTRLEN(bestKey));
+ }
+ return ESR_SUCCESS;
+CLEANUP:
+ return rc;
+}
+
+ESR_ReturnCode PFileSystemCreatePFile(const LCHAR* filename, ESR_BOOL littleEndian, PFile** self)
+{
+ ESR_ReturnCode rc;
+ LCHAR absolutePath[P_PATH_MAX];
+ PFileSystem* fileSystem;
+ size_t len;
+
+ if (filename == NULL || self == NULL)
+ {
+ rc = ESR_INVALID_ARGUMENT;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ LSTRCPY(absolutePath, filename);
+ lstrtrim(absolutePath);
+ len = P_PATH_MAX;
+ CHKLOG(rc, PFileSystemGetAbsolutePath(absolutePath, &len));
+ CHKLOG(rc, PFileSystemGetFS(absolutePath, &fileSystem, NULL));
+ rc = fileSystem->createPFile(fileSystem, absolutePath, littleEndian, self);
+ if (rc == ESR_NO_MATCH_ERROR)
+ rc = ESR_OPEN_ERROR;
+ if (rc != ESR_SUCCESS)
+ {
+ PLogError("%s, %s, %s", ESR_rc2str(rc), filename, absolutePath);
+ goto CLEANUP;
+ }
+ return ESR_SUCCESS;
+CLEANUP:
+ return rc;
+}
+
+ESR_ReturnCode PFileSystemMkdir(const LCHAR* path)
+{
+ ESR_ReturnCode rc;
+ LCHAR absolutePath[P_PATH_MAX];
+ PFileSystem* fileSystem;
+ size_t len;
+
+ if (path == NULL)
+ {
+ rc = ESR_INVALID_ARGUMENT;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ LSTRCPY(absolutePath, path);
+ lstrtrim(absolutePath);
+ len = P_PATH_MAX;
+ CHKLOG(rc, PFileSystemGetAbsolutePath(absolutePath, &len));
+ CHKLOG(rc, PFileSystemGetFS(absolutePath, &fileSystem, NULL));
+ CHK(rc, fileSystem->mkdir(fileSystem, absolutePath));
+ return ESR_SUCCESS;
+CLEANUP:
+ return rc;
+}
+
+ESR_ReturnCode PFileSystemGetcwd(LCHAR* path, size_t* len)
+{
+ ESR_ReturnCode rc;
+
+ if (path == NULL || len == NULL)
+ {
+ rc = ESR_INVALID_ARGUMENT;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ if (LSTRLEN(PFileSystemCurrentDirectory) + 1 > *len)
+ {
+ rc = ESR_BUFFER_OVERFLOW;
+ *len = LSTRLEN(PFileSystemCurrentDirectory) + 1;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ LSTRCPY(path, PFileSystemCurrentDirectory);
+ /* Check function postcondition */
+ passert(path[LSTRLEN(path)-1] == L('/'));
+ return ESR_SUCCESS;
+CLEANUP:
+ return rc;
+}
+
+ESR_ReturnCode PFileSystemChdir(const LCHAR* path)
+{
+ ESR_ReturnCode rc;
+ LCHAR absolutePath[P_PATH_MAX];
+ PFileSystem* fileSystem;
+ size_t len;
+
+ if (path == NULL)
+ {
+ rc = ESR_INVALID_ARGUMENT;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ LSTRCPY(absolutePath, path);
+ /* Ensure path ends with '/' */
+ if (absolutePath[LSTRLEN(absolutePath)-1] != L('/'))
+ LSTRCAT(absolutePath, L("/"));
+ lstrtrim(absolutePath);
+ len = P_PATH_MAX;
+ CHKLOG(rc, PFileSystemGetAbsolutePath(absolutePath, &len));
+ CHKLOG(rc, PFileSystemGetFS(absolutePath, &fileSystem, NULL));
+ rc = fileSystem->chdir(fileSystem, absolutePath);
+ if (rc != ESR_SUCCESS)
+ {
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ if (absolutePath[LSTRLEN(absolutePath)-1] != L('/'))
+ LSTRCAT(absolutePath, L("/"));
+ LSTRCPY(PFileSystemCurrentDirectory, absolutePath);
+ return ESR_SUCCESS;
+CLEANUP:
+ return rc;
+}
+
+/**
+ * PRECONDITION: Directory names must end with '/'
+ */
+ESR_ReturnCode PFileSystemGetParentDirectory(LCHAR* path, size_t* len)
+{
+ LCHAR* lastSlash;
+ LCHAR clone[P_PATH_MAX];
+ ESR_ReturnCode rc;
+ size_t len2;
+
+ if (path == NULL || len == NULL)
+ {
+ rc = ESR_INVALID_ARGUMENT;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ LSTRCPY(clone, path);
+ lstrtrim(clone);
+ len2 = P_PATH_MAX;
+ CHKLOG(rc, PFileSystemGetAbsolutePath(clone, &len2));
+
+ /* 1.0 - Strip filename */
+ lastSlash = LSTRRCHR(clone, L('/'));
+ if (lastSlash == NULL)
+ {
+ /* path contains only a filename */
+ LSTRCPY(path, L("../"));
+ return ESR_SUCCESS;
+ }
+ else if (lastSlash < clone + LSTRLEN(clone) - 1)
+ {
+
+ *(lastSlash + 1) = L('\0');
+ if (LSTRLEN(clone) > *len)
+ {
+ *len = LSTRLEN(clone);
+ rc = ESR_BUFFER_OVERFLOW;
+ goto CLEANUP;
+ }
+ LSTRCPY(path, clone);
+ *len = LSTRLEN(path);
+ return ESR_SUCCESS;
+ }
+
+ /* Get parent directory */
+ if (lastSlash -clone + 2 == 3 && LSTRNCMP(clone, L("../"), 3) == 0)
+ {
+ LSTRCAT(clone, L("../"));
+ if (LSTRLEN(clone) > *len)
+ {
+ *len = LSTRLEN(clone);
+ rc = ESR_BUFFER_OVERFLOW;
+ goto CLEANUP;
+ }
+ LSTRCPY(path, clone);
+ *len = LSTRLEN(path);
+ return ESR_SUCCESS;
+ }
+ if (lastSlash -clone + 1 == 2 && LSTRNCMP(clone, L("./"), 2) == 0)
+ {
+ if (LSTRLEN(L("../")) > *len)
+ {
+ *len = LSTRLEN(L("../"));
+ rc = ESR_BUFFER_OVERFLOW;
+ goto CLEANUP;
+ }
+ LSTRCPY(path, L("../"));
+ *len = LSTRLEN(path);
+ return ESR_SUCCESS;
+ }
+ else if (lastSlash == clone && LSTRNCMP(clone, L("/"), 1) == 0)
+ {
+ rc = ESR_INVALID_ARGUMENT;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ *lastSlash = 0;
+ lastSlash = LSTRRCHR(clone, L('/'));
+ if (lastSlash != NULL)
+ {
+ *(lastSlash + 1) = 0;
+ if (LSTRLEN(clone) > *len)
+ {
+ *len = LSTRLEN(clone);
+ rc = ESR_BUFFER_OVERFLOW;
+ goto CLEANUP;
+ }
+ LSTRCPY(path, clone);
+ *len = LSTRLEN(path);
+ }
+ else
+ {
+ LSTRCPY(path, L(""));
+ *len = 0;
+ }
+ return ESR_SUCCESS;
+CLEANUP:
+ return rc;
+}
+
+ESR_ReturnCode PFileSystemIsDirectoryPath(const LCHAR* path, ESR_BOOL* isDirectory)
+{
+ LCHAR temp[P_PATH_MAX];
+ ESR_ReturnCode rc;
+
+ passert(isDirectory != NULL);
+ LSTRCPY(temp, path);
+ lstrtrim(temp);
+ CHKLOG(rc, PFileSystemCanonicalSlashes(temp));
+ *isDirectory = temp[LSTRLEN(temp)-1] == '/';
+ return ESR_SUCCESS;
+CLEANUP:
+ return rc;
+}
diff --git a/portable/src/PFileSystemImpl.c b/portable/src/PFileSystemImpl.c
new file mode 100644
index 0000000..1b1e00b
--- /dev/null
+++ b/portable/src/PFileSystemImpl.c
@@ -0,0 +1,107 @@
+/*---------------------------------------------------------------------------*
+ * PFileSystemImpl.c *
+ * *
+ * Copyright 2007, 2008 Nuance Communciations, Inc. *
+ * *
+ * 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 "LCHAR.h"
+#include "PFileSystemImpl.h"
+#include "plog.h"
+#include "pmemory.h"
+
+#define MTAG NULL
+
+ESR_BOOL PFileSystemCreated = ESR_FALSE;
+
+/**
+ * [file path, PFileSystem*] mapping.
+ */
+PHashTable* PFileSystemPathMap = NULL;
+
+/**
+ * Portable standard input.
+ */
+PFile* PSTDIN = NULL;
+/**
+ * Portable standard output.
+ */
+PFile* PSTDOUT = NULL;
+/**
+ * Portable standard error.
+ */
+PFile* PSTDERR = NULL;
+
+/**
+ * Current working directory.
+ */
+LCHAR PFileSystemCurrentDirectory[P_PATH_MAX] = L("/");
+
+#ifdef USE_THREAD
+/* Prototype of private function */
+PORTABLE_API ESR_ReturnCode PtrdFlush();
+#endif
+
+
+ESR_ReturnCode PFileSystemCreate(void)
+{
+ ESR_ReturnCode rc;
+
+ if (PFileSystemCreated)
+ return ESR_SUCCESS;
+
+#ifdef USE_STACKTRACE
+ CHKLOG(rc, PStackTraceCreate());
+#endif
+ CHKLOG(rc, PHashTableCreate(NULL, MTAG, &PFileSystemPathMap));
+ CHKLOG(rc, PFileSystemInitializeStreamsImpl());
+ PFileSystemCreated = ESR_TRUE;
+ return ESR_SUCCESS;
+CLEANUP:
+ return rc;
+}
+
+ESR_ReturnCode PFileSystemDestroy(void)
+{
+ ESR_ReturnCode rc;
+ LCHAR* key;
+ PHashTableEntry* entry;
+ PHashTableEntry* oldEntry;
+
+ if (!PFileSystemCreated)
+ return ESR_SUCCESS;
+ PFileSystemCreated = ESR_FALSE;
+ if (PFileSystemPathMap != NULL)
+ {
+ CHKLOG(rc, PHashTableEntryGetFirst(PFileSystemPathMap, &entry));
+ while (entry != NULL)
+ {
+ CHKLOG(rc, PHashTableEntryGetKeyValue(entry, (void **)&key, (void **)NULL));
+ oldEntry = entry;
+ CHKLOG(rc, PHashTableEntryAdvance(&entry));
+ CHKLOG(rc, PHashTableEntryRemove(oldEntry));
+ FREE(key);
+ }
+ CHKLOG(rc, PHashTableDestroy(PFileSystemPathMap));
+ PFileSystemPathMap = NULL;
+ }
+ CHKLOG(rc, PFileSystemShutdownStreamsImpl());
+#ifdef USE_STACKTRACE
+ CHKLOG(rc, PStackTraceDestroy());
+#endif
+ return ESR_SUCCESS;
+CLEANUP:
+ return rc;
+}
diff --git a/portable/src/PFileWrap.c b/portable/src/PFileWrap.c
new file mode 100644
index 0000000..b2fa53a
--- /dev/null
+++ b/portable/src/PFileWrap.c
@@ -0,0 +1,513 @@
+/*---------------------------------------------------------------------------*
+ * PFileWrap.c *
+ * *
+ * Copyright 2007, 2008 Nuance Communciations, Inc. *
+ * *
+ * 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 <stdarg.h>
+#include "LCHAR.h"
+#include "pendian.h"
+#include "PFile.h"
+#include "plog.h"
+#include "pstdio.h"
+#include "ptypes.h"
+
+
+
+
+ESR_ReturnCode PFileClose( PFile *self )
+ {
+
+ fclose ( (FILE *)self );
+ return ( ESR_SUCCESS );
+ }
+
+
+
+ESR_ReturnCode PFileRead ( PFile *self, void *buffer, size_t size, size_t *count )
+ {
+ ESR_ReturnCode read_status;
+ size_t items_read;
+ int ferror_status;
+
+ items_read = fread ( buffer, size, *count, (FILE *)self );
+
+ if ( items_read > 0 )
+ {
+ read_status = ESR_SUCCESS;
+ *count = items_read;
+ }
+ else
+ {
+ ferror_status = ferror ( (FILE *)self );
+
+ if ( ferror_status == 0 )
+ {
+ read_status = ESR_SUCCESS;
+ *count = items_read;
+ }
+ else
+ {
+ read_status = ESR_READ_ERROR;
+ }
+ }
+ return ( read_status );
+ }
+
+
+
+ESR_ReturnCode PFileWrite ( PFile *self, const void *buffer, size_t size, size_t *count )
+ {
+ ESR_ReturnCode write_status;
+ size_t items_written;
+
+ items_written = fwrite ( buffer, size, *count, (FILE *)self );
+
+ if ( items_written == ( *count ) )
+ {
+ write_status = ESR_SUCCESS;
+ *count = items_written;
+ }
+ else
+ {
+ write_status = ESR_READ_ERROR;
+ }
+ return ( write_status );
+ }
+
+
+
+ESR_ReturnCode PFileFlush ( PFile *self )
+ {
+ ESR_ReturnCode flush_status;
+ size_t flush_ok;
+
+ flush_ok = fflush ( (FILE *)self );
+
+ if ( flush_ok == 0 )
+ {
+ flush_status = ESR_SUCCESS;
+ }
+ else
+ {
+ flush_status = ESR_FLUSH_ERROR;
+ }
+ return ( flush_status );
+ }
+
+
+
+ESR_ReturnCode PFileSeek ( PFile *self, long offset, int origin )
+ {
+ ESR_ReturnCode seek_status;
+ size_t seek_ok;
+
+ seek_ok = fseek ( (FILE *)self, offset, origin );
+
+ if ( seek_ok == 0 )
+ {
+ seek_status = ESR_SUCCESS;
+ }
+ else
+ {
+ seek_status = ESR_SEEK_ERROR;
+ }
+ return ( seek_status );
+ }
+
+
+
+ESR_ReturnCode PFileGetPosition ( PFile *self, size_t *position )
+ {
+ ESR_ReturnCode get_status;
+ long ftell_result;
+
+ ftell_result = ftell ( (FILE *)self );
+
+ if ( ftell_result >= 0 )
+ {
+ *position = (size_t)ftell_result;
+ get_status = ESR_SUCCESS;
+ }
+ else
+ {
+ get_status = ESR_INVALID_STATE;
+ }
+ return ( get_status );
+ }
+
+
+
+ESR_ReturnCode PFileIsEOF ( PFile *self, ESR_BOOL *isEof )
+ {
+#ifdef NO_FEOF
+ long posCur; /* remember current file position */
+ long posEnd; /* end of file position */
+
+ posCur = ftell ( self );
+ fseek ( self, 0, SEEK_END );
+ posEnd = ftell ( self );
+
+ if ( posCur == posEnd )
+ *isEof = ESR_TRUE;
+ else
+ *isEof = ESR_FALSE;
+ fseek ( self, posCur, SEEK_SET ); /* restore position in file */
+#else
+ int is_eof;
+
+ is_eof = feof ( (FILE *)self );
+
+ if ( is_eof != 0 )
+ *isEof = ESR_TRUE;
+ else
+ *isEof = ESR_FALSE;
+#endif
+ return ( ESR_SUCCESS );
+ }
+
+
+
+ESR_ReturnCode PFileIsErrorSet ( PFile *self, ESR_BOOL *isError )
+ {
+ int is_error;
+
+ is_error = ferror ( (FILE *)self );
+
+ if ( is_error != 0 )
+ *isError = ESR_TRUE;
+ else
+ *isError = ESR_FALSE;
+ return ( ESR_SUCCESS );
+ }
+
+
+
+ESR_ReturnCode PFileClearError ( PFile *self )
+ {
+
+ clearerr ( (FILE *)self );
+ return ( ESR_SUCCESS );
+ }
+
+
+
+ESR_ReturnCode PFileVfprintf ( PFile *self, int *result, const LCHAR *format, va_list args )
+ {
+ int bytes_printed;
+
+ bytes_printed = vfprintf ( (FILE *)self, format, args );
+
+ if ( result != NULL )
+ *result = bytes_printed;
+ return ( ESR_SUCCESS );
+ }
+
+
+
+ESR_ReturnCode PFileFgetc ( PFile *self, LINT *result )
+ {
+ ESR_ReturnCode fgetc_status;
+ int error_status;
+
+ *result = fgetc ( (FILE *)self );
+
+ if ( ( *result ) != EOF )
+ {
+ fgetc_status = ESR_SUCCESS;
+ }
+ else
+ {
+ error_status = ferror ( (FILE *)self );
+
+ if ( error_status == 0 )
+ fgetc_status = ESR_SUCCESS;
+ else
+ fgetc_status = ESR_INVALID_STATE;
+ }
+ return ( fgetc_status );
+ }
+
+
+
+ESR_ReturnCode PFileFgets ( PFile *self, LCHAR *string, int n, LCHAR **result )
+ {
+ ESR_ReturnCode fgets_status;
+ int error_status;
+ LCHAR *temp;
+
+ temp = fgets ( string, n, (FILE *)self );
+
+ if ( temp != NULL )
+ {
+ fgets_status = ESR_SUCCESS;
+
+ if ( result != NULL )
+ *result = temp;
+ }
+ else
+ {
+ error_status = ferror ( (FILE *)self );
+
+ if ( error_status == 0 )
+ {
+ fgets_status = ESR_SUCCESS;
+
+ if ( result != NULL )
+ *result = NULL;
+ }
+ else
+ fgets_status = ESR_INVALID_STATE;
+ }
+ return ( fgets_status );
+ }
+
+
+
+PFile *pfopen ( const LCHAR *filename, const LCHAR *mode )
+ {
+ PFile *result;
+
+ result = (PFile *)fopen ( filename, mode );
+ return ( result );
+ }
+
+
+
+size_t pfread ( void *buffer, size_t size, size_t count, PFile *stream )
+ {
+ ESR_ReturnCode rc;
+
+ rc = PFileRead ( stream, buffer, size, &count );
+
+ if ( rc != ESR_SUCCESS )
+ return ( 0 );
+ return ( count );
+ }
+
+
+
+size_t pfwrite ( const void *buffer, size_t size, size_t count, PFile *stream )
+ {
+ ESR_ReturnCode rc;
+
+ rc = PFileWrite ( stream, buffer, size, &count );
+ if ( rc != ESR_SUCCESS )
+ return ( 0 );
+ return ( count );
+ }
+
+
+
+int pfclose ( PFile *stream )
+ {
+
+ fclose ( (FILE *)stream );
+
+ return ( 0 );
+ }
+
+
+
+void prewind (PFile *stream)
+ {
+
+ PFileSeek ( stream, 0, SEEK_SET );
+ }
+
+
+
+int pfseek ( PFile *stream, long offset, int origin )
+ {
+ ESR_ReturnCode rc;
+
+ rc = PFileSeek ( stream, offset, origin );
+
+ if ( rc != ESR_SUCCESS )
+ return ( 1 );
+ return ( 0 );
+ }
+
+
+
+long pftell ( PFile *stream )
+ {
+ ESR_ReturnCode rc;
+ size_t result;
+
+ rc = PFileGetPosition ( stream, &result );
+
+ if ( rc != ESR_SUCCESS )
+ return ( -1 );
+ return ( result );
+ }
+
+
+
+int pfeof ( PFile *stream )
+ {
+ ESR_BOOL eof;
+
+ PFileIsEOF ( stream, &eof );
+
+ if ( ! eof )
+ return ( 0 );
+ return ( 1 );
+ }
+
+
+
+int pferror ( PFile *stream )
+ {
+ ESR_BOOL error;
+
+ PFileIsErrorSet ( stream, &error );
+
+ if ( ! error )
+ return ( 0 );
+ return ( 1 );
+ }
+
+
+
+void pclearerr ( PFile *stream )
+ {
+
+ PFileClearError ( stream );
+ }
+
+
+
+int pfflush ( PFile *stream )
+ {
+ ESR_ReturnCode rc;
+
+ rc = PFileFlush ( stream );
+
+ if ( rc != ESR_SUCCESS )
+ return ( PEOF );
+ return ( 0 );
+ }
+
+
+
+LCHAR* pfgets ( LCHAR *string, int n, PFile *self )
+ {
+ LCHAR *result;
+ ESR_ReturnCode rc;
+
+ rc = PFileFgets ( self, string, n, &result );
+
+ if ( rc != ESR_SUCCESS )
+ return ( NULL );
+ return ( result );
+ }
+
+
+
+LINT pfgetc ( PFile *self )
+ {
+ ESR_ReturnCode rc;
+ LINT result;
+
+ rc = PFileFgetc ( self, &result );
+
+ if ( rc != ESR_SUCCESS )
+ return ( PEOF );
+ return ( result );
+ }
+
+
+
+int pfprintf ( PFile *stream, const LCHAR *format, ... )
+ {
+ ESR_ReturnCode rc;
+ int result;
+ va_list args;
+
+ va_start ( args, format );
+ rc = PFileVfprintf ( stream, &result, format, args );
+ va_end ( args );
+
+ if ( rc != ESR_SUCCESS )
+ return ( -1 );
+ return ( result );
+ }
+
+
+
+int pvfprintf ( PFile *stream, const LCHAR *format, va_list argptr )
+ {
+ ESR_ReturnCode rc;
+ int result;
+
+ rc = PFileVfprintf ( stream, &result, format, argptr );
+
+ if ( rc != ESR_SUCCESS )
+ return ( -1 );
+ return ( result );
+ }
+
+
+ESR_ReturnCode pf_convert_backslashes_to_forwardslashes ( LCHAR *string_to_convert )
+ {
+ ESR_ReturnCode rc;
+ int string_status;
+
+ if ( string_to_convert != NULL )
+ {
+ string_status = lstrreplace ( string_to_convert, L('\\'), L('/') );
+
+ if ( string_status == 0 )
+ rc = ESR_SUCCESS;
+ else
+ rc = ESR_INVALID_ARGUMENT;
+ }
+ else
+ {
+ rc = ESR_INVALID_ARGUMENT;
+ }
+ return ( rc );
+ }
+
+
+
+ESR_ReturnCode pf_is_path_absolute ( const LCHAR* input_path, ESR_BOOL* isAbsolute )
+ {
+ ESR_ReturnCode rc;
+ LCHAR path [P_PATH_MAX];
+
+ if ( isAbsolute != NULL )
+ {
+ LSTRCPY ( path, input_path );
+ rc = pf_convert_backslashes_to_forwardslashes ( path );
+
+ if ( rc == ESR_SUCCESS )
+ {
+ if ( ( path [0] == '/' ) || ( ( LISALPHA ( path [0] ) ) && ( path [1] == ':' ) && ( path [2] == '/' ) ) )
+ *isAbsolute = ESR_TRUE;
+ else
+ *isAbsolute = ESR_FALSE;
+ }
+ }
+ else
+ {
+ rc = ESR_INVALID_ARGUMENT;
+ }
+ return ( rc );
+ }
+
diff --git a/portable/src/PStackSize.c b/portable/src/PStackSize.c
new file mode 100644
index 0000000..9679ca6
--- /dev/null
+++ b/portable/src/PStackSize.c
@@ -0,0 +1,61 @@
+/*---------------------------------------------------------------------------*
+ * PStackSize.c *
+ * *
+ * Copyright 2007, 2008 Nuance Communciations, Inc. *
+ * *
+ * 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 "pstdio.h"
+#include "PStackSize.h"
+
+#ifdef _WIN32
+
+static const char * PSTACK_BASE = NULL;
+
+/** Initialize the stack base. This should be done in the main() function.
+ * Note that the local variables of main() are not taken into account in the
+ * stack size computation. To overcome this problem, rewrite main() as a
+ * simple function that first invokes PSTACK_SIZE_INIT and then invokes the
+ * original main().
+*/
+void PSTACK_SIZE_INIT()
+{
+ PSTACK_BASE = (const char *) _alloca(0);
+}
+
+/**
+ * Computes the current stack size. The returned value is the number of bytes
+ * in the stack.
+ **/
+size_t PSTACK_SIZE_GET()
+{
+ return (PSTACK_BASE - ((const char *) _alloca(0)));
+}
+
+#else
+
+/* Insert other platform implementations here... */
+/*
+#error not supported at this time
+*/
+void PSTACK_SIZE_INIT()
+{}
+
+size_t PSTACK_SIZE_GET()
+{
+ return 0;
+}
+
+#endif
diff --git a/portable/src/UNIX/PANSIFileSystemUNIXImpl.c b/portable/src/UNIX/PANSIFileSystemUNIXImpl.c
new file mode 100644
index 0000000..2c561b1
--- /dev/null
+++ b/portable/src/UNIX/PANSIFileSystemUNIXImpl.c
@@ -0,0 +1,166 @@
+/*---------------------------------------------------------------------------*
+ * PANSIFileSystemUNIXImpl.c *
+ * *
+ * Copyright 2007, 2008 Nuance Communciations, Inc. *
+ * *
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+
+#include "errno.h"
+#include "PFileSystemImpl.h"
+#include "PANSIFileSystem.h"
+#include "PANSIFileSystemImpl.h"
+#include "phashtable.h"
+#include "LCHAR.h"
+#include "plog.h"
+
+ESR_ReturnCode PANSIFileSystemGetVirtualPathImpl(PFileSystem* self, LCHAR* path, size_t* len)
+{
+ PANSIFileSystemImpl* impl = (PANSIFileSystemImpl*) self;
+ PHashTableEntry* entry;
+ LCHAR driveLetter = 0;
+ LCHAR* key;
+ LCHAR* value;
+ LCHAR* bestKey = NULL;
+ LCHAR* bestValue = NULL;
+ ESR_BOOL isAbsolute;
+ ESR_ReturnCode rc;
+
+ CHKLOG(rc, lstrtrim(path));
+ CHKLOG(rc, PFileSystemCanonicalSlashes(path));
+ CHKLOG(rc, PFileSystemIsAbsolutePath(path, &isAbsolute));
+ if (isAbsolute && path[0] != L('/'))
+ {
+ /* Skip drive letters in absolute paths */
+ driveLetter = path[0];
+ LSTRCPY(path, path + 2);
+ }
+ CHKLOG(rc, PHashTableEntryGetFirst(impl->directoryMap, &entry));
+ while (entry!=NULL)
+ {
+ CHKLOG(rc, PHashTableEntryGetKeyValue(entry, (void **)&key, (void **)&value));
+ if (LSTRSTR(path, value)==path)
+ {
+ /* File-system handles file path */
+
+ if (bestValue==NULL || LSTRLEN(value) > LSTRLEN(bestValue))
+ {
+ /* Found a better match -- the new key is a subdirectory of the previous bestKey */
+ bestKey = key;
+ bestValue = value;
+ }
+ }
+ CHKLOG(rc, PHashTableEntryAdvance(&entry));
+ }
+ if (bestKey == NULL)
+ {
+ rc = ESR_INVALID_STATE;
+ PLogError(L("PANSIFileSystem does not handle the specified path: %s"), path);
+ goto CLEANUP;
+ }
+
+ /* Delete the real-path */
+ LSTRCPY(path, path + LSTRLEN(bestValue));
+ /* Insert the virtual-path */
+ CHKLOG(rc, lstrinsert(bestKey, path, 0, len));
+
+ /* Bring back the drive letter */
+ if (driveLetter!=0)
+ {
+ CHKLOG(rc, lstrinsert(L("X:/"), path, LSTRLEN(bestKey), len));
+ path[LSTRLEN(bestKey)] = driveLetter;
+ }
+ return ESR_SUCCESS;
+ CLEANUP:
+ return rc;
+}
+ESR_ReturnCode PANSIFileSystemMkdirImpl(PFileSystem* self, const LCHAR* path)
+{
+ LCHAR realPath[P_PATH_MAX];
+ size_t len;
+ ESR_ReturnCode rc;
+
+ passert(path!=NULL);
+ LSTRCPY(realPath, path);
+ len = P_PATH_MAX;
+ CHKLOG(rc, PANSIFileSystemGetRealPathImpl(self, realPath, &len));
+
+ if (mkdir(realPath, S_IRWXU|S_IRWXG|S_IRWXO ) != 0)
+ {
+ switch (errno)
+ {
+ case EEXIST:
+ return ESR_IDENTIFIER_COLLISION;
+ case ENOENT:
+ return ESR_NO_MATCH_ERROR;
+ default:
+ PLogError(L("ESR_INVALID_STATE"));
+ return ESR_INVALID_STATE;
+ }
+ }
+ return ESR_SUCCESS;
+CLEANUP:
+ return rc;
+}
+
+ESR_ReturnCode PANSIFileSystemGetcwdImpl(PFileSystem* self, LCHAR* path, size_t* len)
+{
+ ESR_ReturnCode rc;
+
+ if (path==NULL)
+ {
+ rc = ESR_INVALID_ARGUMENT;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ if (getcwd(path, *len) == NULL)
+ {
+ switch (errno)
+ {
+ case ERANGE:
+ *len = P_PATH_MAX;
+ return ESR_BUFFER_OVERFLOW;
+ case ENOMEM:
+ default:
+ PLogError(L("ESR_INVALID_STATE"));
+ return ESR_INVALID_STATE;
+ }
+ }
+
+ CHKLOG(rc, PANSIFileSystemGetVirtualPathImpl(self, path, len));
+ return ESR_SUCCESS;
+CLEANUP:
+ return rc;
+}
+
+ESR_ReturnCode PANSIFileSystemChdirImpl(PFileSystem* self, const LCHAR* path)
+{
+ LCHAR realPath[P_PATH_MAX];
+ size_t len;
+ ESR_ReturnCode rc;
+
+ passert(path!=NULL);
+ LSTRCPY(realPath, path);
+ len = P_PATH_MAX;
+ CHKLOG(rc, PANSIFileSystemGetRealPathImpl(self, realPath, &len));
+
+ if ((*path != '\0') && (chdir(realPath) != 0))
+ return ESR_NO_MATCH_ERROR;
+ return ESR_SUCCESS;
+CLEANUP:
+ return rc;
+}
diff --git a/portable/src/UNIX/PFileSystemUNIXImpl.c b/portable/src/UNIX/PFileSystemUNIXImpl.c
new file mode 100644
index 0000000..06655f3
--- /dev/null
+++ b/portable/src/UNIX/PFileSystemUNIXImpl.c
@@ -0,0 +1,142 @@
+/*---------------------------------------------------------------------------*
+ * PFileSystemUNIXImpl.c *
+ * *
+ * Copyright 2007, 2008 Nuance Communciations, Inc. *
+ * *
+ * 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 "PANSIFileImpl.h"
+#include "PANSIFileSystemImpl.h"
+#include "PFileSystem.h"
+#include "PFileSystemImpl.h"
+#include "PANSIFileSystem.h"
+#include "phashtable.h"
+#include "plog.h"
+#include "pmemory.h"
+
+
+#ifdef USE_THREAD
+ /* Prototype of private function */
+ PORTABLE_API ESR_ReturnCode PtrdFlush();
+#endif
+
+
+/**
+ * Initializes STDIN, STDOUT, STDERR.
+ */
+ESR_ReturnCode PFileSystemInitializeStreamsImpl(void)
+{
+ ESR_ReturnCode rc;
+ PANSIFileImpl* impl;
+#ifdef USE_THREAD
+ ESR_BOOL threadingEnabled;
+#endif
+ ESR_BOOL isLittleEndian;
+ PANSIFileSystemImpl* ANSIImpl = NULL;
+
+#if __BYTE_ORDER==__LITTLE_ENDIAN
+ isLittleEndian = ESR_TRUE;
+#else
+ isLittleEndian = ESR_FALSE;
+#endif
+ CHKLOG(rc, PANSIFileSystemCreate());
+ ANSIImpl = (PANSIFileSystemImpl*) PANSIFileSystemSingleton;
+ CHKLOG(rc, PMemSetLogEnabled(ESR_FALSE));
+ CHKLOG(rc, PHashTablePutValue(PFileSystemPathMap, L("/"), PANSIFileSystemSingleton, NULL));
+ CHKLOG(rc, PHashTablePutValue(ANSIImpl->directoryMap, L("/"), L("/"), NULL));
+ CHKLOG(rc, PANSIFileSystemSingleton->createPFile(PANSIFileSystemSingleton, L("/dev/stdin"), isLittleEndian, &PSTDIN));
+ impl = (PANSIFileImpl*) PSTDIN;
+ impl->value = stdin;
+
+ CHKLOG(rc, PANSIFileSystemSingleton->createPFile(PANSIFileSystemSingleton, L("/dev/stdout"), isLittleEndian, &PSTDOUT));
+ impl = (PANSIFileImpl*) PSTDOUT;
+ setvbuf(stdout, NULL, _IONBF, 0);
+ impl->value = stdout;
+
+ CHKLOG(rc, PANSIFileSystemSingleton->createPFile(PANSIFileSystemSingleton, L("/dev/stderr"), isLittleEndian, &PSTDERR));
+ impl = (PANSIFileImpl*) PSTDERR;
+ setvbuf(stderr, NULL, _IONBF, 0);
+ impl->value = stderr;
+
+ #ifdef USE_THREAD
+ /* Have STDERR and STDOUT share the same lock */
+ CHKLOG(rc, PtrdIsEnabled(&threadingEnabled));
+ if (threadingEnabled)
+ {
+ CHKLOG(rc, PtrdMonitorDestroy(impl->Interface.lock));
+ impl->Interface.lock = ((PANSIFileImpl*) PSTDOUT)->Interface.lock;
+ }
+ #endif
+ CHKLOG(rc, PHashTableRemoveValue(PFileSystemPathMap, L("/"), NULL));
+ CHKLOG(rc, PHashTableRemoveValue(ANSIImpl->directoryMap, L("/"), NULL));
+ CHKLOG(rc, PMemSetLogEnabled(ESR_TRUE));
+ return ESR_SUCCESS;
+CLEANUP:
+ PHashTableRemoveValue(PFileSystemPathMap, L("/"), NULL);
+ if (ANSIImpl!=NULL)
+ PHashTableRemoveValue(ANSIImpl->directoryMap, L("/"), NULL);
+ PMemSetLogEnabled(ESR_TRUE);
+ return rc;
+}
+
+ESR_ReturnCode PFileSystemShutdownStreamsImpl(void)
+{
+ ESR_ReturnCode rc;
+ PANSIFileImpl* impl;
+
+ /* It is illegal to log to file after the file system has shutdown so we do it now */
+#ifdef USE_THREAD
+ PtrdFlush();
+#endif
+ PMemDumpLogFile();
+
+ if (PSTDIN!=NULL)
+ {
+ CHKLOG(rc, PFileFlush(PSTDIN));
+ impl = (PANSIFileImpl*) PSTDIN;
+ impl->value = NULL;
+ CHKLOG(rc, PFileDestroy(PSTDIN));
+ PSTDIN = NULL;
+ }
+ if (PSTDOUT!=NULL)
+ {
+#ifdef USE_THREAD
+ if (PSTDERR!=NULL)
+ {
+ /* stdout, stderr share the same lock, only one of them should destroy it */
+ PFileImpl* impl = (PFileImpl*) PSTDOUT;
+
+ impl->lock = NULL;
+ }
+#endif
+ CHKLOG(rc, PFileFlush(PSTDOUT));
+ impl = (PANSIFileImpl*) PSTDOUT;
+ impl->value = NULL;
+ CHKLOG(rc, PFileDestroy(PSTDOUT));
+ PSTDOUT = NULL;
+ }
+ if (PSTDERR!=NULL)
+ {
+ CHKLOG(rc, PFileFlush(PSTDERR));
+ impl = (PANSIFileImpl*) PSTDERR;
+ impl->value = NULL;
+ CHKLOG(rc, PFileDestroy(PSTDERR));
+ PSTDERR = NULL;
+ }
+ CHKLOG(rc, PANSIFileSystemDestroy());
+ return ESR_SUCCESS;
+CLEANUP:
+ return rc;
+}
diff --git a/portable/src/UNIX/PFileWrapUNIX_OS_Specific.c b/portable/src/UNIX/PFileWrapUNIX_OS_Specific.c
new file mode 100644
index 0000000..dfe2efe
--- /dev/null
+++ b/portable/src/UNIX/PFileWrapUNIX_OS_Specific.c
@@ -0,0 +1,116 @@
+/*---------------------------------------------------------------------------*
+ * PFileWrapUNIX_OS_Specific.c *
+ * *
+ * Copyright 2007, 2008 Nuance Communciations, Inc. *
+ * *
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+
+#include "errno.h"
+#include "PFileSystemImpl.h"
+#include "PANSIFileSystem.h"
+#include "PANSIFileSystemImpl.h"
+#include "phashtable.h"
+#include "LCHAR.h"
+#include "plog.h"
+
+ESR_ReturnCode pf_make_dir ( const LCHAR* path )
+ {
+ ESR_ReturnCode rc;
+
+ passert(path!=NULL);
+
+ if ( mkdir ( path, S_IRWXU|S_IRWXG|S_IRWXO ) == 0)
+ {
+ rc = ESR_SUCCESS;
+ }
+ else
+ {
+ switch (errno)
+ {
+ case EEXIST:
+ rc = ESR_IDENTIFIER_COLLISION;
+ break;
+
+ case ENOENT:
+ rc = ESR_NO_MATCH_ERROR;
+ break;
+
+ default:
+ PLogError ( L("ESR_INVALID_STATE") );
+ rc = ESR_INVALID_STATE;
+ break;
+ }
+ }
+ return ( rc );
+ }
+
+
+
+ESR_ReturnCode pf_get_cwd ( LCHAR* path, size_t *len )
+ {
+ ESR_ReturnCode rc;
+
+ if ( path != NULL )
+ {
+ if ( getcwd ( path, *len ) != NULL)
+ {
+ rc = ESR_SUCCESS;
+ }
+ else
+ {
+ switch ( errno )
+ {
+ case ERANGE:
+ rc = ESR_BUFFER_OVERFLOW;
+ break;
+
+ case ENOMEM:
+ rc = ESR_OUT_OF_MEMORY;
+ break;
+
+ default:
+ PLogError(L("ESR_INVALID_STATE"));
+ rc = ESR_INVALID_STATE;
+ break;
+ }
+ }
+ }
+ else
+ {
+ rc = ESR_INVALID_ARGUMENT;
+ PLogError(ESR_rc2str(rc));
+ }
+
+ return ( rc );
+ }
+
+
+
+ESR_ReturnCode pf_change_dir ( const LCHAR* path )
+ {
+ ESR_ReturnCode rc;
+
+ passert ( path != NULL );
+ passert ( *path != '\0' );
+
+ if ( chdir ( path ) == 0 )
+ rc = ESR_SUCCESS;
+ else
+ rc = ESR_NO_MATCH_ERROR;
+ return ( rc );
+ }
diff --git a/portable/src/pLastError.c b/portable/src/pLastError.c
new file mode 100644
index 0000000..446be08
--- /dev/null
+++ b/portable/src/pLastError.c
@@ -0,0 +1,65 @@
+/*---------------------------------------------------------------------------*
+ * pLastError.c *
+ * *
+ * Copyright 2007, 2008 Nuance Communciations, Inc. *
+ * *
+ * 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 "pLastError.h"
+#include "plog.h"
+
+void printGetLastErrorInternal(const LCHAR* text, char* file, int line)
+{
+#ifdef _WIN32
+ LCHAR* msg;
+
+ if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */
+ (LPTSTR) &msg,
+ 0,
+ NULL))
+ {
+ ESR_BOOL isInit;
+ ESR_ReturnCode rc;
+ msg[LSTRLEN(msg)-2] = L('\0'); /* cut off newline character */
+
+ rc = PLogIsInitialized(&isInit);
+ if (rc != ESR_SUCCESS)
+ isInit = FALSE;
+ if (isInit)
+ PLogError(L("%s: %s"), text, msg);
+ else
+ pfprintf(PSTDERR, L("[%s:%d] %s: %s\n"), file, line, text, msg);
+ LocalFree(msg);
+ }
+#elif defined(__vxworks)
+ int err;
+
+ err = errnoGet(); /* get the error status value of the calling task */
+#ifndef NDEBUG
+ /* printErrno(err); */ /* need special flag to build Simulator */
+#endif
+ pfprintf(PSTDERR, "[%s:%d] %s, errno = %x\n", file, line, text, err);
+
+#elif (OS == OS_UNIX)
+
+#else
+#error("Have not implemented yet!!!")
+#endif
+}
diff --git a/portable/src/pcputimer.c b/portable/src/pcputimer.c
new file mode 100644
index 0000000..3338475
--- /dev/null
+++ b/portable/src/pcputimer.c
@@ -0,0 +1,238 @@
+/*---------------------------------------------------------------------------*
+ * pcputimer.c *
+ * *
+ * Copyright 2007, 2008 Nuance Communciations, Inc. *
+ * *
+ * 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 "pcputimer.h"
+#include "pmemory.h"
+
+#if defined(_WIN32)
+
+/*
+ Note that this implementation assumes that GetThreadTimes is
+ available (requires NT 3.5 and above) and that 64 bit arithmetic is
+ available (requires VC)
+*/
+
+struct PCPUTimer_t
+{
+ HANDLE hThread;
+ LARGE_INTEGER RefTime;
+ asr_uint32_t elapsed;
+};
+
+
+/**
+ * Creates a new timer object.
+ **/
+ESR_ReturnCode PCPUTimerCreate(PCPUTimer **timer)
+{
+ PCPUTimer *tmp = NULL;
+
+ if (timer == NULL)
+ return ESR_INVALID_ARGUMENT;
+ tmp = NEW(PCPUTimer, "PCPUTimer");
+ if (tmp == NULL) return ESR_OUT_OF_MEMORY;
+
+ tmp->hThread = GetCurrentThread();
+ tmp->RefTime.QuadPart = -1;
+ tmp->elapsed = 0;
+ *timer = tmp;
+
+ return ESR_SUCCESS;
+}
+
+ESR_ReturnCode PCPUTimerDestroy(PCPUTimer *timer)
+{
+ if (timer == NULL) return ESR_INVALID_ARGUMENT;
+ FREE(timer);
+ return ESR_SUCCESS;
+}
+
+/**
+ * Starts the timer. This sets the reference time from which all new elapsed
+ * time are computed. This does not reset the elapsed time to 0. This is
+ * useful to pause the timer.
+ **/
+ESR_ReturnCode PCPUTimerStart(PCPUTimer *timer)
+{
+ FILETIME CreationTime;
+ FILETIME ExitTime;
+ FILETIME KernelTime;
+ FILETIME UserTime;
+
+ if (timer == NULL) return ESR_INVALID_ARGUMENT;
+ if (!GetThreadTimes(timer->hThread,
+ &CreationTime, &ExitTime, &KernelTime, &UserTime))
+ {
+ return ESR_FATAL_ERROR;
+ }
+
+ timer->RefTime.QuadPart = (((LARGE_INTEGER*) & KernelTime)->QuadPart +
+ ((LARGE_INTEGER*) & UserTime)->QuadPart);
+
+ return ESR_SUCCESS;
+}
+
+/**
+ * Stops the timer.
+ **/
+ESR_ReturnCode PCPUTimerStop(PCPUTimer *timer)
+{
+ if (timer == NULL) return ESR_INVALID_ARGUMENT;
+ if (timer->RefTime.QuadPart != -1)
+ {
+ FILETIME CreationTime;
+ FILETIME ExitTime;
+ FILETIME KernelTime;
+ FILETIME UserTime;
+
+ if (!GetThreadTimes(timer->hThread,
+ &CreationTime, &ExitTime, &KernelTime, &UserTime))
+ return ESR_FATAL_ERROR;
+
+ timer->elapsed =
+ (asr_uint32_t) (((LARGE_INTEGER*) &KernelTime)->QuadPart +
+ ((LARGE_INTEGER*) &UserTime)->QuadPart -
+ timer->RefTime.QuadPart) / 10;
+ }
+ return ESR_SUCCESS;
+}
+
+/**
+ * Returns the timer elapsed time. If the Timer is in the stopped state,
+ * successive calls to getElapsed() will always return the same value. If
+ * the Timer is in the started state, successive calls will return the
+ * elapsed time since the last time PCPUTimerStart() was called.
+ */
+ESR_ReturnCode PCPUTimerGetElapsed(PCPUTimer *timer, asr_uint32_t *elapsed)
+{
+ if (timer == NULL || elapsed == NULL) return ESR_INVALID_ARGUMENT;
+ if (timer->RefTime.QuadPart != -1)
+ {
+ FILETIME CreationTime;
+ FILETIME ExitTime;
+ FILETIME KernelTime;
+ FILETIME UserTime;
+
+ if (!GetThreadTimes(timer->hThread,
+ &CreationTime, &ExitTime, &KernelTime, &UserTime))
+ return ESR_FATAL_ERROR;
+
+ *elapsed = timer->elapsed +
+ (asr_uint32_t)(((LARGE_INTEGER*) & KernelTime)->QuadPart +
+ ((LARGE_INTEGER*) & UserTime)->QuadPart -
+ timer->RefTime.QuadPart) / 10;
+ }
+ else
+ *elapsed = timer->elapsed;
+ return ESR_SUCCESS;
+}
+
+
+/**
+ * Resets the elapsed time to 0 and resets the reference time of the Timer.
+ * This effectively reset the timer in the same state it was right after creation.
+ **/
+ESR_ReturnCode PCPUTimerReset(PCPUTimer *timer)
+{
+ if (timer == NULL) return ESR_INVALID_ARGUMENT;
+ timer->RefTime.QuadPart = -1;
+ timer->elapsed = 0;
+ return ESR_SUCCESS;
+}
+
+#elif defined(POSIX)
+/*
+*/
+
+struct PCPUTimer_t
+{
+ HANDLE hThread;
+ asr_uint32_t RefTime;
+ asr_uint32_t elapsed;
+};
+
+/**
+* Creates a new timer object.
+**/
+ESR_ReturnCode PCPUTimerCreate(PCPUTimer **timer)
+{
+ PCPUTimer *tmp = NULL;
+
+ if (timer == NULL) return ESR_INVALID_ARGUMENT;
+ tmp = NEW(PCPUTimer, "PCPUTimer");
+ if (tmp == NULL) return ESR_OUT_OF_MEMORY;
+
+ tmp->hThread = (HANDLE)pthread_self();
+ tmp->elapsed = 0;
+ *timer = tmp;
+
+ return ESR_SUCCESS;
+}
+
+ESR_ReturnCode PCPUTimerDestroy(PCPUTimer *timer)
+{
+ if (timer == NULL) return ESR_INVALID_ARGUMENT;
+ FREE(timer);
+ return ESR_SUCCESS;
+}
+
+/**
+* Starts the timer. This sets the reference time from which all new elapsed
+* time are computed. This does not reset the elapsed time to 0. This is
+* useful to pause the timer.
+**/
+ESR_ReturnCode PCPUTimerStart(PCPUTimer *timer)
+{
+ return ESR_SUCCESS;
+}
+
+/**
+* Stops the timer.
+**/
+ESR_ReturnCode PCPUTimerStop(PCPUTimer *timer)
+{
+ return ESR_SUCCESS;
+}
+
+/**
+* Returns the timer elapsed time. If the Timer is in the stopped state,
+* successive calls to getElapsed() will always return the same value. If
+* the Timer is in the started state, successive calls will return the
+* elapsed time since the last time PCPUTimerStart() was called.
+*/
+ESR_ReturnCode PCPUTimerGetElapsed(PCPUTimer *timer, asr_uint32_t *elapsed)
+{
+ return ESR_SUCCESS;
+}
+
+
+/**
+* Resets the elapsed time to 0 and resets the reference time of the Timer.
+* This effectively reset the timer in the same state it was right after creation.
+**/
+ESR_ReturnCode PCPUTimerReset(PCPUTimer *timer)
+{
+ return ESR_SUCCESS;
+}
+
+#else
+/* #error "Ptimer not implemented for this platform." */
+#endif
diff --git a/portable/src/pcrc.c b/portable/src/pcrc.c
new file mode 100644
index 0000000..6f31f08
--- /dev/null
+++ b/portable/src/pcrc.c
@@ -0,0 +1,159 @@
+/*---------------------------------------------------------------------------*
+ * pcrc.c *
+ * *
+ * Copyright 2007, 2008 Nuance Communciations, Inc. *
+ * *
+ * 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 "pcrc.h"
+#include <limits.h>
+
+/* the CRC tables are computed by the crctable.c program which is part of this
+ distribution but not compiled.
+ */
+
+#if (UINT_MAX >= 0xFFFFFFFFU)
+
+#define WIDTH 32
+
+/* USE CRC-32 algorithm on machine with at least 32 bits. */
+#define POLYNOMIAL 0x04C11DB7
+
+static unsigned int crcTable[] =
+ {
+ 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
+ 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
+ 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
+ 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
+ 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
+ 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
+ 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
+ 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
+ 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
+ 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
+ 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
+ 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
+ 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
+ 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
+ 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
+ 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
+ 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
+ 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
+ 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
+ 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
+ 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
+ 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
+ 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
+ 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
+ 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
+ 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
+ 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
+ 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
+ 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
+ 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
+ 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
+ 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
+ };
+
+#elif (UINT_MAX >= 0xFFFFU)
+/* use CRC-16 on machine with [16..32[ bits integer. */
+
+#define WIDTH 16
+#define TOPBIT (1 << (WIDTH - 1))
+
+#define POLYNOMIAL 0x8005
+
+static unsigned int crcTable[] =
+ {
+ 0x0000, 0x8005, 0x800f, 0x000a, 0x801b, 0x001e, 0x0014, 0x8011,
+ 0x8033, 0x0036, 0x003c, 0x8039, 0x0028, 0x802d, 0x8027, 0x0022,
+ 0x8063, 0x0066, 0x006c, 0x8069, 0x0078, 0x807d, 0x8077, 0x0072,
+ 0x0050, 0x8055, 0x805f, 0x005a, 0x804b, 0x004e, 0x0044, 0x8041,
+ 0x80c3, 0x00c6, 0x00cc, 0x80c9, 0x00d8, 0x80dd, 0x80d7, 0x00d2,
+ 0x00f0, 0x80f5, 0x80ff, 0x00fa, 0x80eb, 0x00ee, 0x00e4, 0x80e1,
+ 0x00a0, 0x80a5, 0x80af, 0x00aa, 0x80bb, 0x00be, 0x00b4, 0x80b1,
+ 0x8093, 0x0096, 0x009c, 0x8099, 0x0088, 0x808d, 0x8087, 0x0082,
+ 0x8183, 0x0186, 0x018c, 0x8189, 0x0198, 0x819d, 0x8197, 0x0192,
+ 0x01b0, 0x81b5, 0x81bf, 0x01ba, 0x81ab, 0x01ae, 0x01a4, 0x81a1,
+ 0x01e0, 0x81e5, 0x81ef, 0x01ea, 0x81fb, 0x01fe, 0x01f4, 0x81f1,
+ 0x81d3, 0x01d6, 0x01dc, 0x81d9, 0x01c8, 0x81cd, 0x81c7, 0x01c2,
+ 0x0140, 0x8145, 0x814f, 0x014a, 0x815b, 0x015e, 0x0154, 0x8151,
+ 0x8173, 0x0176, 0x017c, 0x8179, 0x0168, 0x816d, 0x8167, 0x0162,
+ 0x8123, 0x0126, 0x012c, 0x8129, 0x0138, 0x813d, 0x8137, 0x0132,
+ 0x0110, 0x8115, 0x811f, 0x011a, 0x810b, 0x010e, 0x0104, 0x8101,
+ 0x8303, 0x0306, 0x030c, 0x8309, 0x0318, 0x831d, 0x8317, 0x0312,
+ 0x0330, 0x8335, 0x833f, 0x033a, 0x832b, 0x032e, 0x0324, 0x8321,
+ 0x0360, 0x8365, 0x836f, 0x036a, 0x837b, 0x037e, 0x0374, 0x8371,
+ 0x8353, 0x0356, 0x035c, 0x8359, 0x0348, 0x834d, 0x8347, 0x0342,
+ 0x03c0, 0x83c5, 0x83cf, 0x03ca, 0x83db, 0x03de, 0x03d4, 0x83d1,
+ 0x83f3, 0x03f6, 0x03fc, 0x83f9, 0x03e8, 0x83ed, 0x83e7, 0x03e2,
+ 0x83a3, 0x03a6, 0x03ac, 0x83a9, 0x03b8, 0x83bd, 0x83b7, 0x03b2,
+ 0x0390, 0x8395, 0x839f, 0x039a, 0x838b, 0x038e, 0x0384, 0x8381,
+ 0x0280, 0x8285, 0x828f, 0x028a, 0x829b, 0x029e, 0x0294, 0x8291,
+ 0x82b3, 0x02b6, 0x02bc, 0x82b9, 0x02a8, 0x82ad, 0x82a7, 0x02a2,
+ 0x82e3, 0x02e6, 0x02ec, 0x82e9, 0x02f8, 0x82fd, 0x82f7, 0x02f2,
+ 0x02d0, 0x82d5, 0x82df, 0x02da, 0x82cb, 0x02ce, 0x02c4, 0x82c1,
+ 0x8243, 0x0246, 0x024c, 0x8249, 0x0258, 0x825d, 0x8257, 0x0252,
+ 0x0270, 0x8275, 0x827f, 0x027a, 0x826b, 0x026e, 0x0264, 0x8261,
+ 0x0220, 0x8225, 0x822f, 0x022a, 0x823b, 0x023e, 0x0234, 0x8231,
+ 0x8213, 0x0216, 0x021c, 0x8219, 0x0208, 0x820d, 0x8207, 0x0202
+ };
+
+#else
+#error "CRC is not defined on machine with less than 16 bits arithmetic."
+#endif
+
+unsigned int pcrcComputeData(const void *data, unsigned int size)
+{
+ return pcrcUpdateData(CRC_INITIAL_VALUE, data, size);
+}
+
+unsigned int pcrcUpdateData(unsigned int crc, const void *data, unsigned int size)
+{
+ register unsigned char byte;
+ register const unsigned char *p = (const unsigned char *) data;
+ register unsigned int remainder = crc;
+
+ if (p != NULL)
+ {
+ while (size > 0)
+ {
+#if CHAR_BIT <= 8
+ byte = (unsigned char)((remainder >> (WIDTH - 8)) ^ *p++);
+ remainder = crcTable[byte] ^(remainder << 8);
+#elif CHAR_BIT <= 16
+ byte = (unsigned char)(((remainder >> (WIDTH - 8)) ^(*p >> 8)) & 0xFF);
+ remainder = crcTable[byte] ^(remainder << 8);
+ byte = (unsigned char)(((remainder >> (WIDTH - 8)) ^(*p++ & 0xFF)) & 0xFF);
+ remainder = crcTable[byte] ^(remainder << 8);
+#else
+#error "crcCompute not defined for this platform."
+#endif
+ --size;
+ }
+ }
+
+ return remainder;
+}
+
+unsigned int pcrcComputeString(const LCHAR *str)
+{
+ if (str == NULL)
+ return pcrcComputeData(NULL, 0);
+
+ return pcrcComputeData(str, sizeof(LCHAR) * LSTRLEN(str));
+}
diff --git a/portable/src/pendian.c b/portable/src/pendian.c
new file mode 100644
index 0000000..3649ab0
--- /dev/null
+++ b/portable/src/pendian.c
@@ -0,0 +1,49 @@
+/*---------------------------------------------------------------------------*
+ * pendian.c *
+ * *
+ * Copyright 2007, 2008 Nuance Communciations, Inc. *
+ * *
+ * 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 "pendian.h"
+
+void swap_byte_order(void *buffer, size_t count, size_t itemSize)
+{
+ char *data = (char *) buffer;
+ register char *p, *q, c;
+
+ /* Process every item */
+ while (count > 0)
+ {
+ p = data;
+ q = data + itemSize - 1;
+
+ while (p < q)
+ {
+ c = *p;
+ *p++ = *q;
+ *q-- = c;
+ }
+
+ /* Prepare for next pass */
+ data += itemSize;
+ count--;
+ }
+}
diff --git a/portable/src/phashtable.c b/portable/src/phashtable.c
new file mode 100644
index 0000000..4940c60
--- /dev/null
+++ b/portable/src/phashtable.c
@@ -0,0 +1,560 @@
+/*---------------------------------------------------------------------------*
+ * phashtable.c *
+ * *
+ * Copyright 2007, 2008 Nuance Communciations, Inc. *
+ * *
+ * 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 <string.h>
+
+#include "phashtable.h"
+#include "plog.h"
+#include "pmemory.h"
+#include "pstdio.h"
+
+//extern int strcmp(const char * s1, const char * s2);
+
+#define ALLOC_SIZE 16
+
+struct PHashTableEntry_t
+{
+ const void *key;
+ const void *value;
+ PHashTable *table;
+ unsigned int idx;
+ PHashTableEntry *next;
+ PHashTableEntry *prev;
+ unsigned int hashCode;
+};
+
+typedef struct PHashTableEntryBlock_t
+{
+ PHashTableEntry entries[ALLOC_SIZE];
+ struct PHashTableEntryBlock_t *next;
+}
+PHashTableEntryBlock;
+
+
+struct PHashTable_t
+{
+ PHashTableArgs args;
+ const LCHAR *memoryTag;
+ unsigned int size;
+ float maxLoadFactor;
+ PHashTableEntry **entries;
+ unsigned int threshold;
+ PHashTableEntry *freeList;
+ PHashTableEntryBlock *entryBlock;
+};
+
+#include "pcrc.h"
+
+static unsigned int hashString(const void *key)
+{
+ return ~pcrcComputeString(key);
+}
+
+ESR_ReturnCode PHashTableCreate(PHashTableArgs *args,
+ const LCHAR *memTag,
+ PHashTable **table)
+{
+ PHashTable *tmp;
+ unsigned int i;
+
+ if (table == NULL ||
+ (args != NULL && args->maxLoadFactor <= 0.0))
+ return ESR_INVALID_ARGUMENT;
+
+
+ if ((tmp = NEW(PHashTable, memTag)) == NULL)
+ return ESR_OUT_OF_MEMORY;
+
+ if (args == NULL)
+ {
+ tmp->args.capacity = PHASH_TABLE_DEFAULT_CAPACITY;
+ tmp->args.maxLoadFactor = PHASH_TABLE_DEFAULT_MAX_LOAD_FACTOR;
+ tmp->args.hashFunction = PHASH_TABLE_DEFAULT_HASH_FUNCTION;
+ tmp->args.compFunction = PHASH_TABLE_DEFAULT_COMP_FUNCTION;
+ }
+ else
+ {
+ memcpy(&tmp->args, args, sizeof(PHashTableArgs));
+ }
+ if (tmp->args.hashFunction == PHASH_TABLE_DEFAULT_HASH_FUNCTION)
+ tmp->args.hashFunction = hashString;
+
+ if (tmp->args.compFunction == PHASH_TABLE_DEFAULT_COMP_FUNCTION)
+ tmp->args.compFunction = LSTRCMP;
+
+ tmp->entries = NEW_ARRAY(PHashTableEntry *, tmp->args.capacity, memTag);
+
+ if (tmp->entries == NULL)
+ {
+ FREE(tmp);
+ return ESR_OUT_OF_MEMORY;
+ }
+
+ for (i = tmp->args.capacity; i > 0;)
+ {
+ tmp->entries[--i] = NULL;
+ }
+
+ tmp->memoryTag = memTag;
+ tmp->size = 0;
+ tmp->threshold = (unsigned int)(tmp->args.capacity * tmp->args.maxLoadFactor);
+ tmp->freeList = NULL;
+ tmp->entryBlock = NULL;
+
+ *table = tmp;
+ return ESR_SUCCESS;
+}
+
+ESR_ReturnCode PHashTableDestroy(PHashTable *table)
+{
+ PHashTableEntryBlock *tmp, *block;
+
+ if (table == NULL)
+ return ESR_INVALID_ARGUMENT;
+
+ block = table->entryBlock;
+ while (block != NULL)
+ {
+ tmp = block->next;
+ FREE(block);
+ block = tmp;
+ }
+
+ FREE(table->entries);
+ FREE(table);
+ return ESR_SUCCESS;
+}
+
+ESR_ReturnCode PHashTableGetSize(PHashTable *table,
+ size_t *size)
+{
+ if (table == NULL || size == NULL)
+ return ESR_INVALID_ARGUMENT;
+
+ *size = table->size;
+ return ESR_SUCCESS;
+}
+
+static PHashTableEntry *getEntry(PHashTable *table,
+ const void *key,
+ unsigned int hashCode,
+ unsigned int idx)
+{
+ PHashTableEntry *entry = table->entries[idx];
+
+ if (key == NULL)
+ {
+ while (entry != NULL)
+ {
+ if (entry->key == NULL)
+ return entry;
+
+ entry = entry->next;
+ }
+ }
+ else
+ {
+ while (entry != NULL)
+ {
+ if (entry->hashCode == hashCode && table->args.compFunction(key, entry->key) == 0)
+ return entry;
+
+ entry = entry->next;
+ }
+ }
+
+ return NULL;
+}
+
+static void removeEntry(PHashTableEntry *entry)
+{
+ if (entry->prev == NULL)
+ entry->table->entries[entry->idx] = entry->next;
+ else
+ entry->prev->next = entry->next;
+
+ if (entry->next != NULL)
+ entry->next->prev = entry->prev;
+
+ entry->table->size--;
+
+ entry->next = entry->table->freeList;
+ entry->table->freeList = entry;
+ /* clean up entry for re-use. */
+ entry->key = entry->value = NULL;
+}
+
+ESR_ReturnCode PHashTableGetValue(PHashTable *table, const void *key, void **value)
+{
+ PHashTableEntry *entry;
+ unsigned int hashCode;
+ unsigned int idx;
+
+ if (table == NULL || value == NULL)
+ return ESR_INVALID_ARGUMENT;
+
+ hashCode = table->args.hashFunction(key);
+ idx = hashCode % table->args.capacity;
+ if ((entry = getEntry(table, key, hashCode, idx)) != NULL)
+ {
+ *value = (void *) entry->value;
+ return ESR_SUCCESS;
+ }
+ else
+ {
+ *value = NULL;
+ return ESR_NO_MATCH_ERROR;
+ }
+}
+
+ESR_ReturnCode PHashTableContainsKey(PHashTable *table, const void *key, ESR_BOOL* exists)
+{
+ ESR_ReturnCode rc;
+ PHashTableEntry* entry;
+
+ if (table == NULL || exists == NULL)
+ {
+ rc = ESR_INVALID_ARGUMENT;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ rc = PHashTableGetEntry(table, key, &entry);
+ if (rc == ESR_SUCCESS)
+ *exists = ESR_TRUE;
+ else if (rc == ESR_NO_MATCH_ERROR)
+ *exists = ESR_FALSE;
+ else
+ goto CLEANUP;
+
+ return ESR_SUCCESS;
+CLEANUP:
+ return rc;
+}
+
+ESR_ReturnCode PHashTableGetEntry(PHashTable *table, const void *key, PHashTableEntry **entry)
+{
+ unsigned int hashCode;
+ unsigned int idx;
+ PHashTableEntry* result;
+
+ if (table == NULL || entry == NULL)
+ return ESR_INVALID_ARGUMENT;
+
+ hashCode = table->args.hashFunction(key);
+ idx = hashCode % table->args.capacity;
+
+ result = getEntry(table, key, hashCode, idx);
+ if (result == NULL)
+ return ESR_NO_MATCH_ERROR;
+ *entry = result;
+ return ESR_SUCCESS;
+}
+
+static ESR_ReturnCode PHashTableRehash(PHashTable *table)
+{
+ unsigned int i, idx;
+ unsigned int oldCapacity = table->args.capacity;
+ unsigned int newCapacity = ((oldCapacity << 1) | 0x01);
+ PHashTableEntry *entry, *tmp, *next;
+
+ PHashTableEntry **newEntries =
+ (PHashTableEntry **)
+ REALLOC(table->entries,
+ sizeof(PHashTableEntry *) * newCapacity);
+
+ if (newEntries == NULL)
+ return ESR_OUT_OF_MEMORY;
+
+ table->entries = newEntries;
+ table->args.capacity = newCapacity;
+ table->threshold = (unsigned int)(newCapacity * table->args.maxLoadFactor);
+
+ for (i = oldCapacity; i < newCapacity; ++i)
+ {
+ table->entries[i] = NULL;
+ }
+
+ for (i = 0; i < oldCapacity; i++)
+ {
+ for (entry = table->entries[i]; entry != NULL;)
+ {
+ idx = entry->hashCode % newCapacity;
+ if (idx != i)
+ {
+ /* Need to change location. */
+ entry->idx = idx;
+
+ next = entry->next;
+
+ if (entry->prev != NULL)
+ entry->prev->next = next;
+ else
+ table->entries[i] = next;
+
+ if (next != NULL)
+ next->prev = entry->prev;
+
+ tmp = table->entries[idx];
+ entry->next = tmp;
+ entry->prev = NULL;
+ if (tmp != NULL)
+ tmp->prev = entry;
+ table->entries[idx] = entry;
+
+ entry = next;
+ }
+ else
+ {
+ /* Already in the right slot. */
+ entry = entry->next;
+ }
+ }
+ }
+ return ESR_SUCCESS;
+}
+
+
+ESR_ReturnCode PHashTablePutValue(PHashTable *table,
+ const void *key,
+ const void *value,
+ void **oldValue)
+{
+ ESR_ReturnCode rc = ESR_SUCCESS;
+ unsigned int hashCode, idx;
+ PHashTableEntry *entry;
+
+ if (table == NULL) return ESR_INVALID_ARGUMENT;
+ hashCode = table->args.hashFunction(key);
+ idx = hashCode % table->args.capacity;
+
+ entry = getEntry(table, key, hashCode, idx);
+ if (entry != NULL)
+ {
+ if (oldValue != NULL) *oldValue = (void *) entry->value;
+ entry->value = value;
+ return ESR_SUCCESS;
+ }
+
+ /* If we get here, we need to add a new entry. But first, verify if we need
+ to rehash. */
+ if (table->size >= table->threshold)
+ {
+ if ((rc = PHashTableRehash(table)) != ESR_SUCCESS)
+ return rc;
+ idx = hashCode % table->args.capacity;
+ }
+
+ if (table->freeList == NULL)
+ {
+ /* Allocate a new block and put all entries on the free list. */
+ PHashTableEntryBlock *block;
+ int i;
+
+ block = NEW(PHashTableEntryBlock, table->memoryTag);
+ if (block == NULL)
+ return ESR_OUT_OF_MEMORY;
+
+ block->next = table->entryBlock;
+ table->entryBlock = block;
+
+ for (i = 0; i < ALLOC_SIZE - 1; ++i)
+ {
+ block->entries[i].next = &block->entries[i+1];
+ }
+ block->entries[ALLOC_SIZE-1].next = NULL;
+
+ /* do not see any bug in following code. But on the VxWorks with optimization option -O3
+ it produces wrong result: block->entries[0].next is correct but block->entries[1].next = NULL
+ it causes lot of memory wastes.
+ for (i = 0, entry = block->entries; i < ALLOC_SIZE - 1; ++i, ++entry)
+ {
+ entry->next = entry+1;
+ }
+ entry->next = table->freeList;
+ */
+
+ table->freeList = block->entries;
+ }
+
+ /* Get an entry from the freeList. */
+ entry = table->freeList;
+ table->freeList = entry->next;
+
+ /* Initialize entry data structure. */
+ entry->table = table;
+ entry->idx = idx;
+ entry->key = key;
+ entry->value = value;
+ entry->hashCode = hashCode;
+ entry->next = table->entries[idx];
+ entry->prev = NULL;
+ if (entry->next != NULL)
+ entry->next->prev = entry;
+ table->entries[idx] = entry;
+ table->size++;
+
+ if (oldValue != NULL) *oldValue = NULL;
+ return ESR_SUCCESS;
+}
+
+
+ESR_ReturnCode PHashTableRemoveValue(PHashTable *table,
+ const void *key,
+ void **oldValue)
+{
+ unsigned int hashCode, idx;
+ PHashTableEntry *entry;
+ ESR_ReturnCode rc;
+
+ if (table == NULL)
+ {
+ rc = ESR_INVALID_ARGUMENT;
+ PLogError(ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+
+ hashCode = table->args.hashFunction(key);
+ idx = hashCode % table->args.capacity;
+
+ entry = getEntry(table, key, hashCode, idx);
+ if (entry != NULL)
+ {
+ if (oldValue != NULL)
+ *oldValue = (void*) entry->value;
+ removeEntry(entry);
+ }
+ else
+ {
+ if (oldValue != NULL)
+ *oldValue = NULL;
+ }
+ return ESR_SUCCESS;
+CLEANUP:
+ return rc;
+}
+
+ESR_ReturnCode PHashTableEntryGetKeyValue(PHashTableEntry *entry,
+ void **key,
+ void **value)
+{
+ if (entry == NULL) return ESR_INVALID_ARGUMENT;
+
+ if (key != NULL) *key = (void *) entry->key;
+ if (value != NULL) *value = (void *) entry->value;
+ return ESR_SUCCESS;
+}
+
+
+/**
+ * Sets the value associated with this entry.
+ * @param entry The hashtable entry.
+ * @param value The value to associate with the entry.
+ * @param oldvalue If this pointer is non-NULL, it will be set to the previous value associated with this entry.
+ **/
+ESR_ReturnCode PHashTableEntrySetValue(PHashTableEntry *entry,
+ const void *value,
+ void **oldValue)
+{
+ if (entry == NULL) return ESR_INVALID_ARGUMENT;
+
+ if (oldValue != NULL) *oldValue = (void *) entry->value;
+ entry->value = value;
+ return ESR_SUCCESS;
+}
+
+
+/**
+ * Removes the entry from its hash table.
+ *
+ * @param entry The hashtable entry.
+ **/
+ESR_ReturnCode PHashTableEntryRemove(PHashTableEntry *entry)
+{
+ if (entry == NULL)
+ return ESR_INVALID_ARGUMENT;
+
+ removeEntry(entry);
+
+ return ESR_SUCCESS;
+}
+
+static PHashTableEntry* iteratorAdvance(PHashTable *table, PHashTableEntry *entry)
+{
+ unsigned int idx;
+
+ if (entry != NULL)
+ {
+ idx = entry->idx;
+ entry = entry->next;
+ if (entry == NULL)
+ {
+ while (++idx < table->args.capacity)
+ {
+ if (table->entries[idx] != NULL)
+ {
+ entry = table->entries[idx];
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ for (idx = 0; idx < table->args.capacity; ++idx)
+ {
+ if (table->entries[idx] != NULL)
+ {
+ entry = table->entries[idx];
+ break;
+ }
+ }
+ }
+ return entry;
+}
+
+
+ESR_ReturnCode PHashTableEntryGetFirst(PHashTable *table, PHashTableEntry **entry)
+{
+ if (table == NULL || entry == NULL)
+ return ESR_INVALID_ARGUMENT;
+
+ *entry = iteratorAdvance(table, NULL);
+ return ESR_SUCCESS;
+}
+
+/**
+ * Iterates on the next key and value. Returns a NULL key when at the end of the hash table.
+ *
+ * @param iter The iterator on which the iteration is performed.
+ * @param key Returns the key associated with the entry, cannot be NULL.
+ * @param value If non-NULL, returns the value associated with the entry.
+ **/
+ESR_ReturnCode PHashTableEntryAdvance(PHashTableEntry **entry)
+{
+ if (entry == NULL || *entry == NULL)
+ return ESR_INVALID_ARGUMENT;
+
+ *entry = iteratorAdvance((*entry)->table, *entry);
+ return ESR_SUCCESS;
+}
diff --git a/portable/src/plog.c b/portable/src/plog.c
new file mode 100644
index 0000000..3aefbc7
--- /dev/null
+++ b/portable/src/plog.c
@@ -0,0 +1,538 @@
+/*---------------------------------------------------------------------------*
+ * plog.c *
+ * *
+ * Copyright 2007, 2008 Nuance Communciations, Inc. *
+ * *
+ * 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 <stdarg.h>
+#include "PFileSystem.h"
+#include "ptypes.h"
+#include "plog.h"
+#include "pmemory.h"
+#include "pstdio.h"
+#include "ptimestamp.h"
+#include "passert.h"
+#ifdef USE_STACKTRACE
+#include "PStackTrace.h"
+#endif
+
+#ifdef USE_THREAD
+#include "ptrd.h"
+#include "pmutex.h"
+#endif
+
+
+#if defined (ANDROID)
+#if defined (HAVE_ANDROID_OS)
+#define LOG_TAG "Srec"
+#include <utils/Log.h>
+#endif
+#endif
+
+#include "phashtable.h"
+
+#define MTAG __FILE__
+
+#define FILTER_MSG_1 "ESR_BUFFER_OVERFLOW"
+#define FILTER_MSG_1_SIZE ( sizeof ( FILTER_MSG_1 ) - 1 )
+
+#define FILTER_MSG_2 "ESR_NO_MATCH_ERROR"
+#define FILTER_MSG_2_SIZE ( sizeof ( FILTER_MSG_2 ) - 1 )
+
+static unsigned int GlogLevel = 0;
+static PLogger *Glogger = NULL;
+static LOG_OUTPUT_FORMAT GlogFormat = LOG_OUTPUT_FORMAT_MODULE_NAME |
+ LOG_OUTPUT_FORMAT_DATE_TIME;
+/**
+ * Used to detect endless recursion where the PLog module calls itself.
+ */
+static ESR_BOOL locked = ESR_FALSE;
+#ifdef USE_THREAD
+
+static PtrdMutex* Gmutex = NULL;
+#endif
+
+typedef struct FileLogger_t
+{
+ PLogger base;
+ PFile* fp;
+}
+FileLogger;
+
+/**
+ * Prints and formats a message to the log.
+ *
+ * @param self the PLogger.
+ *
+ * @param format the format string specifying the next arguments (a la
+ * printf).
+ *
+ * @param args variable argument list.
+ *
+ * @return The number of bytes written to the PLogger or -1 if an error
+ * occurs.
+ */
+static ESR_ReturnCode FileLoggerPrintf(PLogger *self, const LCHAR *format, ...)
+{
+ FileLogger *p = STATIC_CAST(self, FileLogger, base);
+ ESR_ReturnCode rc;
+ va_list args;
+
+ va_start(args, format);
+ rc = pvfprintf(p->fp, format, args);
+ va_end(args);
+ return rc;
+}
+
+static ESR_ReturnCode FileLoggerFlush(PLogger *self)
+{
+ FileLogger *p = STATIC_CAST(self, FileLogger, base);
+ return pfflush(p->fp) == 0 ? ESR_SUCCESS : ESR_FATAL_ERROR;
+}
+
+
+/**
+ * Destroys the logger. This function is responsible to deallocate any
+ * resources used by the logger. In particular, if buffering is internally
+ * used, it needs to flush the buffer.
+ */
+static void FileLoggerDestroy(PLogger *self)
+{
+ FileLogger *p = STATIC_CAST(self, FileLogger, base);
+ pfflush(p->fp);
+
+ if (p->fp != PSTDERR && p->fp != PSTDOUT)
+ pfclose(p->fp);
+ FREE(p);
+}
+
+static ESR_ReturnCode createPFileLogger(PFile* fp, PLogger** logger)
+{
+ FileLogger* fileLogger;
+
+ if (fp == NULL)
+ return ESR_INVALID_ARGUMENT;
+ fileLogger = NEW(FileLogger, MTAG);
+ if (fileLogger == NULL)
+ return ESR_OUT_OF_MEMORY;
+
+ fileLogger->base.printf = FileLoggerPrintf;
+ fileLogger->base.flush = FileLoggerFlush;
+ fileLogger->base.destroy = FileLoggerDestroy;
+ fileLogger->fp = fp;
+
+ *logger = &fileLogger->base;
+ return ESR_SUCCESS;
+}
+
+/**
+ * Initializes the LOG library. This function must be called before any
+ * logging can take place.
+ *
+ * @param logger The logger to be used to output the messages. If NULL, then
+ * logging goes to PSTDERR. @param logLevel The level of logging requested.
+ *
+ * @return ESR_SUCCESS if success, anything else if an error occurs.
+ *
+ */
+ESR_ReturnCode PLogInit(PLogger *logger, unsigned int logLevel)
+{
+ ESR_ReturnCode rc = ESR_SUCCESS;
+
+ if (Glogger != NULL)
+ return ESR_INVALID_STATE;
+
+ GlogLevel = logLevel;
+
+#ifdef USE_THREAD
+ if ((rc = PtrdMutexCreate(&Gmutex)) != ESR_SUCCESS)
+ return rc;
+#endif
+
+ if (logger != NULL)
+ Glogger = logger;
+ else
+ {
+ rc = createPFileLogger(PSTDERR, &Glogger);
+ if (rc != ESR_SUCCESS)
+ goto CLEANUP;
+ }
+
+ return rc;
+CLEANUP:
+#ifdef USE_THREAD
+ if (Gmutex != NULL)
+ {
+ PtrdMutexDestroy(Gmutex);
+ Gmutex = NULL;
+ }
+#endif
+ return rc;
+}
+
+ESR_ReturnCode PLogIsInitialized(ESR_BOOL* isInit)
+{
+ if (isInit == NULL)
+ return ESR_INVALID_STATE;
+ *isInit = Glogger != NULL;
+ return ESR_SUCCESS;
+}
+
+ESR_ReturnCode PLogIsLocked(ESR_BOOL* isLocked)
+{
+ if (isLocked == NULL)
+ return ESR_INVALID_STATE;
+ *isLocked = locked;
+ return ESR_SUCCESS;
+}
+
+/**
+ * Shutdowns the LOG library. Once this function is called, no logging activity can be performed.
+ * Also, the logger that was given to pLogInit is destroyed.
+ *
+ * @return ESR_SUCCESS if success, anything else if an error occurs.
+ *
+ */
+ESR_ReturnCode PLogShutdown()
+{
+ ESR_ReturnCode rc = ESR_SUCCESS;
+
+ if (Glogger == NULL)
+ return ESR_INVALID_STATE;
+
+#ifdef USE_THREAD
+ if ((rc = PtrdMutexDestroy(Gmutex)) != ESR_SUCCESS)
+ return rc;
+ Gmutex = NULL;
+#endif
+
+ if (Glogger->flush != NULL)
+ Glogger->flush(Glogger);
+ Glogger->destroy(Glogger);
+ Glogger = NULL;
+ return rc;
+}
+
+ESR_ReturnCode PLogGetLevel(unsigned int *logLevel)
+{
+ if (Glogger == NULL)
+ return ESR_INVALID_STATE;
+ if (logLevel == NULL)
+ return ESR_INVALID_ARGUMENT;
+
+ *logLevel = GlogLevel;
+ return ESR_SUCCESS;
+}
+
+ESR_ReturnCode PLogSetLevel(unsigned int logLevel)
+{
+ if (Glogger == NULL)
+ return ESR_INVALID_STATE;
+
+ GlogLevel = logLevel;
+ return ESR_SUCCESS;
+}
+
+#define TIME_BUF_SIZE 24
+#define TIME_FORMAT L("%Y/%m/%d %H:%M:%S")
+#define PLOG_PANIC(x, rc) \
+ do \
+ { \
+ { \
+ pfprintf(PSTDERR, L("[%s:%d] %s failed with %s\n"), __FILE__, __LINE__, x, ESR_rc2str(rc)); \
+ pfflush(PSTDERR); \
+ } \
+ } while (0)
+
+static ESR_ReturnCode logIt(const LCHAR *format, va_list args, ESR_BOOL showStackTrace)
+{
+ ESR_ReturnCode rc = ESR_SUCCESS;
+ ESR_ReturnCode flushRC = ESR_SUCCESS;
+#ifdef USE_STACKTRACE
+#define BUFFER_SIZE P_MAX_STACKTRACE + 2000
+#else
+#define BUFFER_SIZE 2000
+#endif
+ LCHAR buffer[BUFFER_SIZE] = L("");
+
+ // TODO: Remove once logging subsystem supports "warn" level
+ if (strstr(format, "ESR_BUFFER_OVERFLOW")==format)
+ return ESR_SUCCESS;
+
+#ifdef USE_STACKTRACE
+ if (Glogger == NULL)
+ {
+ /*
+ * There are three possible scenerios for why logging would occur although the PLog module
+ * is uninitialized:
+ *
+ * 1) The code fails before PLog is initialized (perhaps in other portable components)
+ * 2) The user forgets to initialize the PLog module
+ * 3) The code fails after PLog is uninitialized (on shutdown)
+ *
+ * We do our best by logging any errors but this might result in the memory leak of
+ * the PStackTrace module in case 3.
+ */
+ rc = PStackTraceCreate();
+ if (rc != ESR_SUCCESS)
+ {
+ PLOG_PANIC(L("PStackTraceCreate"), rc);
+ goto CLEANUP;
+ }
+ }
+ else
+ {
+#ifdef USE_THREAD
+ rc = PtrdMutexLock(Gmutex);
+ if (rc != ESR_SUCCESS)
+ return rc;
+#endif
+ }
+ if (locked)
+ return ESR_INVALID_STATE;
+ locked = ESR_TRUE;
+
+ if (GlogFormat & LOG_OUTPUT_FORMAT_DATE_TIME)
+ {
+ PTimeStamp now;
+ struct tm* loctime;
+ LCHAR timeStr[TIME_BUF_SIZE];
+ size_t timeStrSize;
+
+ PTimeStampSet(&now);
+ loctime = localtime(&now.secs);
+ timeStrSize = LSTRFTIME(timeStr, TIME_BUF_SIZE, TIME_FORMAT, loctime);
+ passert(timeStrSize == (TIME_BUF_SIZE - 5));
+ psprintf(timeStr + (TIME_BUF_SIZE - 5), ".%03hu", now.msecs);
+
+ psprintf(buffer + LSTRLEN(buffer), L("%s|"), timeStr);
+ passert(LSTRLEN(buffer) < BUFFER_SIZE);
+ }
+
+ if (GlogFormat & LOG_OUTPUT_FORMAT_THREAD_ID)
+ {
+ rc = psprintf(buffer + LSTRLEN(buffer), L("trd=%u|"), PtrdGetCurrentThreadId());
+ passert(LSTRLEN(buffer) < BUFFER_SIZE);
+ }
+
+ if (GlogFormat & LOG_OUTPUT_FORMAT_MODULE_NAME && showStackTrace)
+ {
+ size_t len = P_MAX_STACKTRACE;
+ LCHAR text[P_MAX_STACKTRACE];
+ LCHAR* index;
+ size_t i;
+
+ rc = PStackTraceGetValue((LCHAR*) & text, &len);
+ if (rc == ESR_SUCCESS)
+ {
+ for (i = 0; i < 2; ++i)
+ {
+ rc = PStackTracePopLevel((LCHAR*) & text);
+ if (rc != ESR_SUCCESS)
+ {
+ PLOG_PANIC(L("PStackTracePopLevel"), rc);
+ goto CLEANUP;
+ }
+ }
+ index = text;
+ while (index)
+ {
+ index = LSTRSTR(index, L(" at\n"));
+ if (index != NULL)
+ {
+ *(index + 1) = L('<');
+ *(index + 2) = L('-');
+ *(index + 3) = L(' ');
+ }
+ }
+ }
+ else if (rc == ESR_NOT_SUPPORTED)
+ LSTRCPY(text, L(""));
+ else if (rc != ESR_SUCCESS)
+ {
+ PLOG_PANIC(L("PStackTraceGetValue"), rc);
+ goto CLEANUP;
+ }
+ rc = psprintf(buffer + LSTRLEN(buffer), L("Module=%s|"), text);
+ passert(LSTRLEN(buffer) < BUFFER_SIZE);
+ }
+
+ pvsprintf(buffer + LSTRLEN(buffer), format, args);
+#else
+ pvsprintf(buffer + LSTRLEN(buffer), format, args);
+#endif
+ passert(LSTRLEN(buffer) < BUFFER_SIZE);
+
+ psprintf(buffer + LSTRLEN(buffer), L("\n"));
+ passert(LSTRLEN(buffer) < BUFFER_SIZE);
+
+ if (Glogger != NULL)
+ {
+ rc = Glogger->printf(Glogger, L("%s"), buffer);
+ if (rc != ESR_SUCCESS)
+ goto CLEANUP;
+ flushRC = Glogger->flush(Glogger);
+ }
+ else
+ {
+ /* We need to log but the logging module is disabled or is locked so we output to stderr instead */
+ {
+ pfprintf(PSTDERR, L("%s"), buffer);
+ pfflush(PSTDERR);
+ }
+ }
+ locked = ESR_FALSE;
+#ifdef USE_THREAD
+ PtrdMutexUnlock(Gmutex);
+#endif
+ return flushRC;
+CLEANUP:
+ if (Glogger != NULL && Glogger->flush != NULL)
+ flushRC = Glogger->flush(Glogger);
+ locked = ESR_FALSE;
+#ifdef USE_THREAD
+ PtrdMutexUnlock(Gmutex);
+#endif
+ return rc != ESR_SUCCESS ? rc : flushRC;
+}
+
+/**
+ * Conditionally PLogs a message. The message is logged only if module is enabled.
+ *
+ * @param msg The message format specification (ala printf).
+ * @return ESR_SUCCESS if success, anything else if an error occurs.
+ */
+ESR_ReturnCode PLogMessage(const char* msg, ...)
+{
+ va_list args;
+ ESR_ReturnCode rc;
+#if USE_STACKTRACE
+ size_t depth;
+#endif
+
+#if defined (ANDROID)
+#if defined (HAVE_ANDROID_OS)
+ return ( ESR_SUCCESS );/* Get rid of this for phone device */
+#endif
+#endif
+
+ if (Glogger == NULL)
+ return ESR_INVALID_STATE;
+#ifdef USE_STACKTRACE
+ return ESR_SUCCESS;
+ rc = PStackTraceGetDepth(&depth);
+
+ if (rc == ESR_NOT_SUPPORTED)
+ {
+ /* Debugging symbols are missing */
+ return ESR_SUCCESS;
+ }
+ else if (rc != ESR_SUCCESS)
+ {
+ pfprintf(PSTDERR, L("PStackTraceGetDepth"), ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+
+ /* Remove PLogMessage() from depth */
+ --depth;
+ if (GlogLevel < depth)
+ return ESR_SUCCESS;
+#endif
+
+ va_start(args, msg);
+ rc = logIt(msg, args, ESR_FALSE);
+ va_end(args);
+ return rc;
+#if USE_STACKTRACE
+CLEANUP:
+ return rc;
+#endif
+}
+
+
+/**
+ * Unconditionally logs an error message.
+ *
+ * @param msg The message format specification (ala printf).
+ * @return ESR_SUCCESS if success, anything else if an error occurs.
+ */
+ESR_ReturnCode PLogError(const char* msg, ...)
+{
+ va_list args;
+ ESR_ReturnCode rc;
+#if defined (ANDROID)
+#if defined (HAVE_ANDROID_OS)
+ char log_text [2048];
+#endif
+#endif
+
+ va_start(args, msg);
+#if defined (ANDROID)
+#if defined (HAVE_ANDROID_OS)
+ pvsprintf ( log_text, msg, args);
+/* We need to disable some error messages because they are frequently not
+ * errors but due to sloppy use of some functions. This prevents us from
+ * getting flooded with bad error messages. SteveR
+ */
+ if ( ( strncmp ( log_text, FILTER_MSG_1, FILTER_MSG_1_SIZE ) != 0 ) &&
+ ( strncmp ( log_text, FILTER_MSG_2, FILTER_MSG_2_SIZE ) != 0 ) )
+ {
+ LOGE ( log_text );
+ }
+ rc = 0;
+#else
+ rc = logIt(msg, args, ESR_TRUE);
+#endif
+#else
+ rc = logIt(msg, args, ESR_TRUE);
+#endif
+ va_end(args);
+
+ return rc;
+}
+
+
+
+ESR_ReturnCode PLogCreateFileLogger(PFile* file, PLogger **logger)
+{
+ if (logger == NULL || file == NULL)
+ return ESR_INVALID_ARGUMENT;
+
+ return createPFileLogger(file, logger);
+}
+
+/**
+ * Creates a logger that logs to a circular file.
+ *
+ * @param filename The name of the file to be created.
+ * @param maxsize The maximum number of bytes that the file may have.
+ * @param logger logger handle receiving the created logger.
+ */
+ESR_ReturnCode PLogCreateCircularFileLogger(const LCHAR *filename,
+ unsigned int maxsize,
+ PLogger **logger)
+{
+ return ESR_NOT_SUPPORTED;
+}
+
+
+ESR_ReturnCode PLogSetFormat(LOG_OUTPUT_FORMAT format)
+{
+ GlogFormat = format;
+ return ESR_SUCCESS;
+}
diff --git a/portable/src/pmalloc.c b/portable/src/pmalloc.c
new file mode 100644
index 0000000..eaf8007
--- /dev/null
+++ b/portable/src/pmalloc.c
@@ -0,0 +1,610 @@
+/*---------------------------------------------------------------------------*
+ * pmalloc.c *
+ * *
+ * Copyright 2007, 2008 Nuance Communciations, Inc. *
+ * *
+ * 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 source file is only used when PORTABLE_DINKUM_MEM_MGR is defined
+ */
+#ifdef PORTABLE_DINKUM_MEM_MGR
+
+#include <stdlib.h>
+#include <string.h> /* for memset */
+#include "pmalloc.h"
+#include "passert.h"
+#include "ptypes.h"
+#include "plog.h"
+
+#undef malloc
+#undef calloc
+#undef realloc
+#undef free
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ /*
+ * There are two controlling options within this scheme:
+ *
+ * STATIC_MEMORY_POOL: When defined, there is a static array from which memory is
+ * allocated. The size of this array is defined at compile time. When undefined
+ * (the default), the memory is allocated via malloc(). This code works under PSOS and
+ * PSOSIM, but has not been tested anywhere else (4March02).
+ * VOYAGER_KERNEL_MEMORY: When defined for the Voyager platform, it is similar to the
+ * non-static memory pool, but the memory buffer is pre-defined, and is simply pointed
+ * at by the pmalloc initializer.
+ * RTXC_PARTITION_MEMORY: When defined for the RTXC operating system, uses a static kernel
+ * partition resource for the memory chunk.
+ * VOYAGER_KERNEL_MEMORY and RTXC_PARTITION_MEMORY are mutually exclusive and take precedence
+ * over STATIC_MEMORY.
+ *
+
+ * the standard off-the-shelf Dinkumware software is pretty slow, primarily due to
+ * scanning the free-memory linked list in PortFree(). If SPEEDUP is defined, then
+ * split the memory pool into imaginary 'bins', and keep track of the first free list
+ * entry in each bin. Then the linked list lookup can be MUCH faster, as you can
+ * start very close to the final destination in the linked list.
+ *
+ * (If SPEEDUP_COMPARE is defined, then run BOTH the standard technique and the
+ * speedup technique and compare the results.)
+ */
+
+ /* malloc function */
+ _STD_BEGIN
+
+ /* data *******************************************************************************/
+
+#if defined(PORTABLE_DINKUM_MEM_MGR) || defined(PORTABLE_FIXED_SIZE_MEM_BLOCK_SCHEME)
+ /* Verify that memory pool actually was created, because of the lack of structure, this is accessed externally */
+ ESR_ReturnCode memory_pool_creation_status = ESR_FATAL_ERROR;
+#endif
+
+ /* static data */
+ _Altab _Aldata = {0}; /* heap initially empty */
+ psize_t _Size_block = {SIZE_BLOCK}; /* preferred _Getmem chunk */
+
+ /* Memory pool size */
+#define MEM_SIZE_MB( mbytes ) ((mbytes) * 1024 * 1024 )
+
+#ifndef MEM_SIZE
+ /* If not defined on the command line, use default values. */
+#define MEM_SIZE MEM_SIZE_MB( 5 )
+#endif
+
+ /* Memory pool initialized */
+ static int pmallocInitted = FALSE; /* TRUE once initialized */
+
+#ifdef STATIC_MEMORY_POOL
+ /* The memory pool can either be statically allocated or require a one-time system malloc.
+ * For VTB, the system was taking 2 seconds to zero the static memBuffer[] array at
+ * boot time, since it's in the BSS segment. Therefore, for VTB, it is better to allocate
+ * at run time.
+ */
+ static char memBuffer[MEM_SIZE];
+#else
+ static char *memBuffer;
+#endif
+
+ static psize_t memSize = MEM_SIZE;
+
+ /* Memory pool free list */
+ /* partition memory range into 'bins', and keep track of the first
+ * free list entry in each bin. This is to speed up the linked-list search
+ * that takes place when memory is freed.
+ */
+#define BIN_BITS 14 /* smaller number ==> more bins */
+#define BIN_SIZE 16384 /* 2 ^ BIN_BITS */
+
+#define __NUM_MEM_BINS(memPoolSize) (((memPoolSize)/BIN_SIZE) + 5) /* 5 = extra for roundoff */
+#define GET_MEM_BIN( _ptr_ ) (int)(((unsigned int)_ptr_ - (unsigned int)&memBuffer[0]) >> BIN_BITS)
+
+#define NUM_MEM_BINS __NUM_MEM_BINS(MEM_SIZE)
+static _Cell *binsFirstFreeCell[NUM_MEM_BINS+1] = {0};
+ static psize_t numMemBins;
+
+ /* Memory Pool sbrk/getmem variables */
+
+ static char *__heap_ptr = NULL;
+ static char *__heap_end = NULL;
+ static int maxMemUsed = 0;
+
+ /* Memory Pool initialization and _GetMem functions ************************************/
+
+#if _USE_EXISTING_SYSTEM_NAMES
+#define _Sbrk sbrk
+#endif
+
+ _STD_BEGIN
+
+ void *_Sbrk(int incr)
+ {
+ char *ret;
+
+ /* Subtract 1 from __heap_ptr so that the left hand side of the comparison evaluates to the address of the
+ last address of the requested memory block */
+ if ((__heap_ptr + incr - 1) > __heap_end) return(void *) - 1;
+
+ ret = __heap_ptr;
+ __heap_ptr += incr;
+ maxMemUsed += incr;
+ return (void *)ret;
+ }
+
+ void *_Getmem(psize_t size)
+ { /* allocate raw storage */
+ void *p;
+ int isize = size;
+
+ return (isize <= 0 || (p = _Sbrk(isize)) == (void *) - 1
+ ? 0 : p);
+ }
+ _STD_END
+
+ /* PortMallocInit() : initialize memory pool. There is no initialization needed for
+ * a static memory pool. Otherwise, perform a one-time malloc from the OS.
+ */
+ void PortMallocInit(void)
+ {
+#if defined STATIC_MEMORY_POOL
+ memSize = MEM_SIZE;
+#else
+ /* TODO: is malloc of binsFirstFreeCell safe under PSOS in all conditions ? */
+ memBuffer = (char *)malloc(memSize);
+#if defined(PORTABLE_DINKUM_MEM_MGR) || defined(PORTABLE_FIXED_SIZE_MEM_BLOCK_SCHEME)
+ if (memBuffer != NULL) /* For external access, check comment at top */
+ memory_pool_creation_status = ESR_SUCCESS;
+#endif
+ numMemBins = __NUM_MEM_BINS(memSize);
+#endif /* #ifdef VOYAGER_KERNEL_MEMORY */
+
+ passert(memBuffer != NULL);
+ passert(binsFirstFreeCell != NULL);
+
+ __heap_ptr = &memBuffer[0];
+ __heap_end = &memBuffer[memSize-1];
+
+ /* set initted flag so we only do this once */
+ pmallocInitted = TRUE;
+ maxMemUsed = 0;
+
+ memset(&_Aldata, 0, sizeof(_Altab));
+
+ memset(binsFirstFreeCell, 0, sizeof(_Cell*)*(NUM_MEM_BINS + 1));
+ }
+
+ void PortMallocTerm(void)
+ {
+#ifndef STATIC_MEMORY_POOL
+ memSize = 0;
+ free(memBuffer);
+ memBuffer = NULL;
+#if defined(PORTABLE_DINKUM_MEM_MGR) || defined(PORTABLE_FIXED_SIZE_MEM_BLOCK_SCHEME)
+ memory_pool_creation_status = ESR_FATAL_ERROR; /* For external access, check comment at top */
+#endif
+#endif
+ pmallocInitted = FALSE;
+ }
+
+ /* PortGetMaxMemUsed() : return the maximum real memory allocated.
+ * There is another function of the same name in pmemory.cpp, for tracking
+ * psos block memory. It uses #ifdef MEM_PSOS_BLOCK_SCHEME to enable.
+ */
+ int PortMallocGetMaxMemUsed(void)
+ {
+ return maxMemUsed;
+ }
+
+ /* PortMallocSetPoolSize( psize_t size ) : define size of memory pool.
+ */
+ void PortMallocSetPoolSize(psize_t size)
+ {
+#if !defined(STATIC_MEMORY_POOL) && !defined(VOYAGER_KERNEL_MEMORY) && !defined(RTXC_PARTITION_MEMORY)
+ if (!pmallocInitted)
+ {
+ memSize = size;
+ }
+#else
+ (void)size;
+#endif
+ }
+
+ /* PortMallocGetPoolSize() : return size of memory pool.
+ */
+ psize_t PortMallocGetPoolSize(void)
+ {
+#if defined STATIC_MEMORY_POOL
+ return MEM_SIZE;
+#else
+ return memSize;
+#endif
+ }
+
+ /* debug *******************************************************************************/
+
+ /* xalloc.h internal header - debugging components */
+#if DEBUG
+
+ int _OK_Cell(_Cell *p)
+ {
+ passert(SIZE_CELL <= p->_Size);
+ return 1;
+ }
+
+ typedef struct _DB_Altab
+ {
+ psize_t total_heap;
+ psize_t total_alloc;
+ psize_t total_free;
+ }
+ _DB_Altab;
+
+ _DB_Altab _DB_Altab_object = {0};
+
+ void _UPD_Altab(psize_t d_heap, psize_t d_alloc, psize_t d_free)
+ {
+ _DB_Altab *pd = &_DB_Altab_object;
+ pd->total_heap += d_heap;
+ pd->total_alloc += d_alloc;
+ pd->total_free += d_free;
+ }
+
+ int _OK_Altab(_Altab *p)
+ {
+ _DB_Altab *pd = &_DB_Altab_object;
+ _Cell *q;
+ psize_t total_free = 0;
+ if (p->_Head == 0)
+ return 1;
+ for (q = p->_Head; q != 0; q = q->_Next)
+ {
+ total_free += q->_Size;
+ _OK_Cell(q);
+ if (q->_Next != 0)
+ {
+ passert(_PTR_NORM((char *)q + q->_Size) <=
+ _PTR_NORM((char *)q->_Next));
+ passert(_PTR_NORM(q) < _PTR_NORM(q->_Next));
+ }
+ }
+ passert(pd->total_heap == pd->total_alloc + pd->total_free);
+ passert(total_free == pd->total_free);
+ return 1;
+ }
+#endif /* DEBUG */
+
+ /* allocation functions ***************************************************************/
+
+ static _Cell **findmem(psize_t size)
+ { /* find storage */
+ _Cell *q, **qb;
+
+ for (; ;)
+ { /* check freed space first */
+ if ((qb = _Aldata._Plast) == 0)
+ { /* take it from the top */
+ for (qb = &_Aldata._Head; *qb != 0;
+ qb = &(*qb)->_Next)
+ if (size <= (*qb)->_Size)
+ return (qb);
+ }
+ else
+ { /* resume where we left off */
+ for (; *qb != 0; qb = &(*qb)->_Next)
+ if (size <= (*qb)->_Size)
+ return (qb);
+ q = *_Aldata._Plast;
+ for (qb = &_Aldata._Head; *qb != q;
+ qb = &(*qb)->_Next)
+ if (size <= (*qb)->_Size)
+ return (qb);
+ }
+ { /* try to buy more space */
+ psize_t bs;
+
+ for (bs = _Size_block; ; bs >>= 1)
+ { /* try larger blocks first */
+ if (bs < size)
+ bs = size;
+ if ((q = (_Cell *)_Getmem(bs)) != 0)
+ break;
+ else if (bs == size)
+ return (0); /* no storage */
+ }
+ /* got storage: add to heap and retry */
+ q->_Size = bs;
+ _UPD_Altab(q->_Size, q->_Size, 0); /* heap=alloc+free */
+ PortFree((char *)q + CELL_OFF);
+ }
+ }
+ }
+
+
+ void *(PortMalloc)(psize_t size_arg)
+ { /* allocate a data object on the heap */
+ _Cell *q, **qb;
+#ifdef SPEEDUP
+ int qbsBin;
+ int qbNextBin;
+#endif /* SPEEDUP */
+ psize_t size;
+
+ passert(pmallocInitted);
+
+ size = (size_arg + (CELL_OFF + M_MASK)) & ~M_MASK;
+
+ _OK_Altab(&_Aldata);
+ if (size <= size_arg)
+ return (0); /* size_arg too large */
+ if (size < SIZE_CELL) /* round up size */
+ size = SIZE_CELL;
+ if ((qb = findmem(size)) == 0)
+ return (0);
+ q = *qb;
+ if (q->_Size - SIZE_CELL < size)
+ {
+ /* use entire cell (there's not enough space to carve out a new cell from this one) */
+#ifdef SPEEDUP
+ /* remove *qb cell from free list.
+ * careful : the Next pointer may be in a different bin.
+ */
+ qbsBin = GET_MEM_BIN(*qb);
+
+ /* Check whether the cell is at the end of the 'free' linked-list */
+ if (0 != ((*qb)->_Next))
+ {
+ /* The cell is not at the end of the free linked-list; find out which bin the next free cell is in */
+ qbNextBin = GET_MEM_BIN((*qb)->_Next);
+
+ if (qbsBin == qbNextBin)
+ {
+ if (binsFirstFreeCell[qbsBin] == *qb)
+ {
+ /* The allocated cell was the first free cell in the bin; update the first free cell
+ pointer to point to the next free cell */
+ binsFirstFreeCell[qbsBin] = (*qb)->_Next;
+ }
+ }
+ else
+ {
+ if (binsFirstFreeCell[qbsBin] == *qb)
+ {
+ /* The allocated cell was the only free cell in the bin; update the first free cell
+ pointer to point to NULL */
+
+ binsFirstFreeCell[qbsBin] = 0;
+ }
+ }
+ }
+ else
+ {
+ /* Cell is at the end of the 'free' linked-list */
+ if (binsFirstFreeCell[qbsBin] == *qb)
+ {
+ /* The allocated cell was the first free cell in the bin; there are no following free cells
+ so set the first free cell pointer to NULL */
+ binsFirstFreeCell[qbsBin] = 0;
+ }
+ }
+#endif /* SPEEDUP */
+ *qb = q->_Next;
+ }
+ else
+ { /* peel off a residual cell */
+ *qb = (_Cell *)((char *)q + size);
+ (*qb)->_Next = q->_Next;
+ (*qb)->_Size = q->_Size - size;
+ q->_Size = size;
+#ifdef SPEEDUP
+ /* remove q from free list, and add *qb to free list.
+ * Do this as two separate steps because they may be in 2 different bins.
+ */
+ /* remove q from free list */
+ if (binsFirstFreeCell[GET_MEM_BIN(q)] == q)
+ binsFirstFreeCell[GET_MEM_BIN(q)] = 0;
+ /* now add *qb to its bin's free list if it's the first */
+ qbsBin = GET_MEM_BIN(*qb);
+ if ((binsFirstFreeCell[qbsBin] == 0) || (*qb < binsFirstFreeCell[qbsBin]))
+ binsFirstFreeCell[qbsBin] = *qb;
+#endif /* SPEEDUP */
+ }
+ _Aldata._Plast = qb; /* resume scan here */
+ _UPD_Altab(0, q->_Size, -q->_Size); /* heap=alloc+free */
+ _OK_Altab(&_Aldata);
+ return ((char *)q + CELL_OFF);
+ }
+ _STD_END
+
+
+ /* free function */
+ _STD_BEGIN
+
+ void(PortFree)(void *ptr)
+ { /* free an allocated data object */
+ register _Cell *q;
+ register psize_t size;
+#ifdef SPEEDUP
+ int binNum;
+ int binIndex;
+ int qNextBin;
+ int qNextNextBin;
+#endif /* SPEEDUP */
+ static int portFreeCount = 0;
+ portFreeCount++;
+
+ passert(pmallocInitted);
+
+ _OK_Altab(&_Aldata);
+ if (ptr == 0)
+ return;
+ q = (_Cell *)((char *)ptr - CELL_OFF);
+ size = q->_Size;
+#ifdef SPEEDUP
+ binNum = GET_MEM_BIN(q);
+#endif /* SPEEDUP */
+ if (size < SIZE_CELL || (size & M_MASK) != 0)
+ return; /* erroneous call, bad count */
+ if (_Aldata._Head == 0
+ || _PTR_NORM(q) < _PTR_NORM(_Aldata._Head))
+ { /* insert at head of list */
+ q->_Next = _Aldata._Head;
+ _Aldata._Head = q;
+#ifdef SPEEDUP
+ /* always the start of a bin */
+ binsFirstFreeCell[binNum] = q;
+#endif /* SPEEDUP */
+ }
+ else
+ { /* scan for insertion point */
+ register _Cell *qp = _Aldata._Head;
+ register char *qpp;
+ register _Cell *nextCell;
+#if !defined SPEEDUP || defined SPEEDUP_COMPARE
+ _Cell *savedQp;
+
+ /* this search loop is where all the time is spent */
+ while ((nextCell = qp->_Next) != 0
+ && _PTR_NORM(nextCell) < _PTR_NORM(q))
+ qp = qp->_Next;
+ savedQp = qp;
+#endif /* SPEEDUP */
+
+#ifdef SPEEDUP
+ /* this is where the SPEEDUP code is sped up : start with the bin's first free cell */
+ _Cell *firstFreeInBin = binsFirstFreeCell[binNum];
+ if ((firstFreeInBin != 0) && (q > firstFreeInBin))
+ {
+ qp = firstFreeInBin;
+ while ((nextCell = qp->_Next) != 0
+ && _PTR_NORM(nextCell) < _PTR_NORM(q))
+ {
+ qp = qp->_Next;
+ }
+ }
+ else
+ {
+ /* go back to the previous non-zero bin */
+ qp = NULL; /* for diagnostics */
+ for (binIndex = binNum; binIndex >= 0; binIndex--)
+ {
+ if ((binsFirstFreeCell[binIndex] != 0) && (q > binsFirstFreeCell[binIndex]))
+ {
+ qp = binsFirstFreeCell[binIndex];
+ break;
+ }
+ }
+ /* this code for diagnostic purposes to see how often it happens. otherwise,
+ * qp could have been set to _Aldata._Head prior to the binIndex loop above.
+ */
+ if (qp == NULL)
+ {
+ qp = _Aldata._Head;
+ }
+
+ /* find the free cell location */
+ while ((nextCell = qp->_Next) != 0
+ && _PTR_NORM(nextCell) < _PTR_NORM(q))
+ qp = qp->_Next;
+ }
+
+#ifdef SPEEDUP_COMPARE
+ if (qp != savedQp)
+ printf("oops \n");
+#endif /* SPEEDUP_COMPARE */
+#endif /* SPEEDUP */
+
+#if !defined SPEEDUP || defined SPEEDUP_COMPARE
+ qp = savedQp;
+#endif /* SPEEDUP */
+
+ qpp = (char *)qp + qp->_Size;
+ if (_PTR_NORM((char *)q) < _PTR_NORM(qpp))
+ return; /* erroneous call, overlaps qp */
+ else if (_PTR_NORM(qpp) == _PTR_NORM((char *)q))
+ { /* merge qp and q (merge with prior cell) */
+ /* nothing to do to bin's free list here */
+ qp->_Size += q->_Size;
+ q = qp;
+ }
+ else if (qp->_Next != 0 && _PTR_NORM((char *)qp->_Next)
+ < _PTR_NORM((char *)q + q->_Size))
+ return; /* erroneous call, overlaps qp->_Next */
+ else
+ { /* splice q after qp */
+#ifdef SPEEDUP
+ /* add 1 entry here - this could change first entry in q's bin */
+ _Cell *firstFree = binsFirstFreeCell[binNum];
+ if ((firstFree == 0) || (q < firstFree))
+ binsFirstFreeCell[binNum] = q;
+#endif /* SPEEDUP */
+ q->_Next = qp->_Next;
+ qp->_Next = q;
+ }
+ }
+ if (q->_Next != 0 && _PTR_NORM((char *)q + q->_Size)
+ == _PTR_NORM((char *)q->_Next))
+ { /* merge q and q->_Next (merge with latter cell) */
+#ifdef SPEEDUP
+ /* lose 1 cell here - this could change first entry in bin.
+ * if q->_Next was first in bin, now it's its Next.
+ * careful : watch for next being in a different bin.
+ */
+ qNextBin = GET_MEM_BIN(q->_Next);
+
+ if (binsFirstFreeCell[qNextBin] == q->_Next)
+ {
+ /* The q->_Next cell is the first free cell in its bin; set the first free cell
+ pointer to NULL for now; if there is another free cell in the same bin then
+ the first free cell pointer will be updated in next 'if' code block */
+ binsFirstFreeCell[qNextBin] = 0;
+
+ /* If there is another free cell after q->_Next and it's in the same bin then
+ update the first free cell pointer if necessary */
+ if (0 != (q->_Next->_Next))
+ {
+ qNextNextBin = GET_MEM_BIN(q->_Next->_Next);
+
+ /* The first free cell pointer for q->_Next->_Next's bin can only be set to 0
+ by the code above; if it is 0 then q->_Next->_Next must be the first free
+ cell in the bin */
+ if (0 == binsFirstFreeCell[qNextNextBin])
+ {
+ binsFirstFreeCell[qNextNextBin] = q->_Next->_Next;
+ }
+ }
+ }
+#endif /* SPEEDUP */
+ _Aldata._Plast = 0; /* deoptimize for safety */
+ q->_Size += q->_Next->_Size;
+ q->_Next = q->_Next->_Next;
+ }
+ _UPD_Altab(0, -size, size); /* heap=alloc+free */
+ _OK_Altab(&_Aldata);
+ /* every successful free "falls off" here */
+ }
+ _STD_END
+
+#ifdef __cplusplus
+} /* end extern "C" */
+#endif
+
+#endif /* PORTABLE_DINKUM_MEM_MGR */
+
+
diff --git a/portable/src/pmemblock.c b/portable/src/pmemblock.c
new file mode 100644
index 0000000..5f3277d
--- /dev/null
+++ b/portable/src/pmemblock.c
@@ -0,0 +1,544 @@
+/*---------------------------------------------------------------------------*
+ * pmemblock.c *
+ * *
+ * Copyright 2007, 2008 Nuance Communciations, Inc. *
+ * *
+ * 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 "pmemory.h"
+#include "ptypes.h"
+
+#if PORTABLE_MEM_MGR == PORTABLE_PSOS_BLOCK_SCHEME_MEM_MGR
+
+#ifdef PSOSIM
+#define PSOS
+#endif
+
+#ifdef PSOS
+#include <stdlib.h>
+#include <psos.h>
+#endif
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ /* Data *****************************************************************/
+
+#define NUM_POOL_BINS 32
+#define NUM_POOL_SLOTS 8
+
+ typedef struct memory_pools
+ {
+ uint32 currentNumberOfPools;
+
+ struct pool_info
+ {
+ unsigned long poolId;
+ void* pMemory;
+ unsigned long size;
+ }
+ poolInfo[NUM_POOL_SLOTS];
+
+ }
+ MEMORY_POOL;
+
+ static MEMORY_POOL memoryPool[NUM_POOL_BINS];
+
+#define NUM_TRACKING_BINS NUM_POOL_BINS
+
+ /* Object tracking variables */
+ static struct tracking_struct
+ {
+ uint32 sCurrentAllocationSize;
+ uint32 sMaximumAllocationSize;
+ uint32 sTotalAllocationSize;
+
+ uint32 sCurrentAllocRealSize;
+ uint32 sMaximumAllocRealSize;
+ uint32 sTotalAllocRealSize;
+
+ uint32 sCurrentAllocationNumber;
+ uint32 sMaximumAllocationNumber;
+ uint32 sTotalAllocationNumber;
+
+ uint32 sCurrentAllocationNumberArray[NUM_TRACKING_BINS];
+ uint32 sMaximumAllocationNumberArray[NUM_TRACKING_BINS];
+ uint32 sTotalAllocationNumberArray[NUM_TRACKING_BINS];
+
+ uint32 sCurrentAllocationSizeArray[NUM_TRACKING_BINS];
+ uint32 sMaximumAllocationSizeArray[NUM_TRACKING_BINS];
+ uint32 sTotalAllocationSizeArray[NUM_TRACKING_BINS];
+ }
+ gMemoryTracking;
+
+
+ /* Functions *********************************************************/
+
+ static uint32 findBin(size_t size)
+ {
+ int i, bin;
+ for (i = 0, bin = 1; i < NUM_TRACKING_BINS; i++, bin <<= 1)
+ {
+ if ((int)size <= bin)
+ return i;
+ }
+
+ return 0;
+ }
+
+
+ static void MemoryTrackingInit(void)
+ {
+ int i;
+ /* Initialization of object tracking variables */
+ gMemoryTracking.sCurrentAllocationSize = 0;
+ gMemoryTracking.sMaximumAllocationSize = 0;
+ gMemoryTracking.sTotalAllocationSize = 0;
+
+ gMemoryTracking.sCurrentAllocationNumber = 0;
+ gMemoryTracking.sMaximumAllocationNumber = 0;
+ gMemoryTracking.sTotalAllocationNumber = 0;
+
+ gMemoryTracking.sCurrentAllocRealSize = 0;
+ gMemoryTracking.sMaximumAllocRealSize = 0;
+ gMemoryTracking.sTotalAllocRealSize = 0;
+
+ for (i = 0; i < NUM_TRACKING_BINS; i++)
+ {
+ gMemoryTracking.sCurrentAllocationNumberArray[i] = 0;
+ gMemoryTracking.sMaximumAllocationNumberArray[i] = 0;
+ gMemoryTracking.sTotalAllocationNumberArray[i] = 0;
+
+ gMemoryTracking.sCurrentAllocationSizeArray[i] = 0;
+ gMemoryTracking.sMaximumAllocationSizeArray[i] = 0;
+ gMemoryTracking.sTotalAllocationSizeArray[i] = 0;
+ }
+ }
+
+
+ static void MemoryTrackingAdd(size_t size)
+ {
+ /* Memory tracking code */
+ uint32 bin = findBin(size);
+ uint32 binsize = 1 << bin;
+ uint32 dummy;
+
+ /* for breakpoint setting */
+#ifdef PSOSIM
+ if (bin == 0)
+ dummy = 0;
+ if (bin == 1)
+ dummy = 0;
+ if (bin == 2)
+ dummy = 0;
+ if (bin == 3)
+ dummy = 0;
+ if (bin == 4)
+ dummy = 0;
+ if (bin == 5)
+ dummy = 0;
+ if (bin == 6)
+ dummy = 0;
+ if (bin == 7)
+ dummy = 0;
+ if (bin == 8)
+ dummy = 0;
+ if (bin == 9)
+ dummy = 0;
+ if (bin == 10)
+ dummy = 0;
+ if (bin == 11)
+ dummy = 0;
+ if (bin == 12)
+ dummy = 0;
+ if (bin == 13)
+ dummy = 0;
+ if (bin == 14)
+ dummy = 0;
+ if (bin == 15)
+ dummy = 0;
+ if (bin == 16)
+ dummy = 0;
+ if (bin == 17)
+ dummy = 0;
+ if (bin == 18)
+ dummy = 0;
+ if (bin == 19)
+ dummy = 0;
+ if (bin == 20)
+ dummy = 0;
+ if (bin == 21)
+ dummy = 0;
+ if (bin > 21)
+ dummy = 0;
+#endif /* PSOSIM */
+
+ gMemoryTracking.sCurrentAllocationSize += size;
+ gMemoryTracking.sTotalAllocationSize += size;
+ if (gMemoryTracking.sCurrentAllocationSize > gMemoryTracking.sMaximumAllocationSize)
+ gMemoryTracking.sMaximumAllocationSize = gMemoryTracking.sCurrentAllocationSize;
+
+ gMemoryTracking.sCurrentAllocRealSize += binsize;
+ gMemoryTracking.sTotalAllocRealSize += binsize;
+ if (gMemoryTracking.sCurrentAllocRealSize > gMemoryTracking.sMaximumAllocRealSize)
+ gMemoryTracking.sMaximumAllocRealSize = gMemoryTracking.sCurrentAllocRealSize;
+
+ gMemoryTracking.sCurrentAllocationNumber++;
+ gMemoryTracking.sTotalAllocationNumber++;
+ if (gMemoryTracking.sCurrentAllocationNumber > gMemoryTracking.sMaximumAllocationNumber)
+ gMemoryTracking.sMaximumAllocationNumber = gMemoryTracking.sCurrentAllocationNumber;
+
+ gMemoryTracking.sCurrentAllocationSizeArray[bin] += size;
+ gMemoryTracking.sTotalAllocationSizeArray[bin] += size;
+ if (gMemoryTracking.sCurrentAllocationSizeArray[bin] > gMemoryTracking.sMaximumAllocationSizeArray[bin])
+ gMemoryTracking.sMaximumAllocationSizeArray[bin] = gMemoryTracking.sCurrentAllocationSizeArray[bin];
+
+ gMemoryTracking.sCurrentAllocationNumberArray[bin]++;
+ gMemoryTracking.sTotalAllocationNumberArray[bin]++;
+ if (gMemoryTracking.sCurrentAllocationNumberArray[bin] > gMemoryTracking.sMaximumAllocationNumberArray[bin])
+ gMemoryTracking.sMaximumAllocationNumberArray[bin] = gMemoryTracking.sCurrentAllocationNumberArray[bin];
+ }
+
+
+ static void MemoryTrackingDelete(unsigned long size)
+ {
+ /* Memory tracking code */
+ uint32 bin = findBin(size);
+ uint32 binsize = 1 << bin;
+
+ gMemoryTracking.sCurrentAllocationSize -= size;
+ gMemoryTracking.sCurrentAllocationNumber--;
+
+ gMemoryTracking.sCurrentAllocationSizeArray[bin] -= size;
+ gMemoryTracking.sCurrentAllocationNumberArray[bin]--;
+
+ gMemoryTracking.sCurrentAllocRealSize -= binsize;
+ }
+
+
+ static void InitPools(void)
+ {
+ int i, j;
+ for (i = 0; i < NUM_POOL_BINS; i++)
+ {
+ memoryPool[i].currentNumberOfPools = 0;
+
+ for (j = 0; j < NUM_POOL_SLOTS; j++)
+ {
+ memoryPool[i].poolInfo[j].poolId = 0;
+ memoryPool[i].poolInfo[j].pMemory = NULL;
+ memoryPool[i].poolInfo[j].size = 0;
+ }
+ }
+ }
+
+
+ static void TermPools(void)
+ {
+ int i, j;
+ /* For some reason, deleting the region then freeing the memory causes a failure */
+ /* TODO: Figure out why??? */
+ for (i = 1; i < NUM_POOL_BINS; i++)
+ {
+ for (j = 0; j < (int)memoryPool[i].currentNumberOfPools; j++)
+ {
+ if (memoryPool[i].poolInfo[j].pMemory != NULL)
+ {
+ unsigned long retval = pt_delete(memoryPool[i].poolInfo[j].poolId);
+ PORT_ASSERT(retval == 0);
+
+ PORT_ASSERT_GOOD_WRITE_POINTER(memoryPool[i].poolInfo[j].pMemory);
+ free(memoryPool[i].poolInfo[j].pMemory);
+
+ memoryPool[i].poolInfo[j].poolId = 0;
+ memoryPool[i].poolInfo[j].pMemory = NULL;
+ memoryPool[i].poolInfo[j].size = 0;
+ }
+ }
+
+ memoryPool[i].currentNumberOfPools = 0;
+ }
+ }
+
+
+#define PARTITION_CONTROL_BLOCK_SIZE 0x400
+
+ static BOOL CreatePool(uint32 whichPool, uint32 poolSize)
+ {
+ static uint32 poolNumber = 0;
+
+ void* pMemory = NULL;
+ unsigned long poolId, unused;
+
+ uint32 currentNumberOfPools = memoryPool[whichPool].currentNumberOfPools;
+
+ PORT_ASSERT((whichPool >= 0) && (whichPool < NUM_POOL_BINS));
+
+ if (currentNumberOfPools == NUM_POOL_SLOTS)
+ return FALSE;
+
+
+ if (whichPool < 2)
+ {
+ /* Invalid partition size */
+ return FALSE;
+ }
+ else
+ {
+ char name[5];
+ unsigned long retval;
+
+ pMemory = malloc(poolSize * (1 << whichPool) + PARTITION_CONTROL_BLOCK_SIZE);
+ PORT_ASSERT_GOOD_WRITE_POINTER(pMemory);
+
+ /* No memory protection */
+ if (pMemory == NULL)
+ {
+ /* No memory left in system */
+ return FALSE;
+ }
+
+
+ sprintf(name, "DP%02d", poolNumber);
+
+ retval = pt_create(name, pMemory, 0, poolSize * (1 << whichPool) + PARTITION_CONTROL_BLOCK_SIZE,
+ 1 << whichPool, PT_LOCAL | PT_DEL, &poolId, &unused);
+ if (retval != 0)
+ {
+ /* Unable to create a pSOS partition */
+ return FALSE;
+ }
+ }
+
+ memoryPool[whichPool].poolInfo[currentNumberOfPools].poolId = poolId;
+ memoryPool[whichPool].poolInfo[currentNumberOfPools].pMemory = pMemory;
+ memoryPool[whichPool].poolInfo[currentNumberOfPools].size = poolSize;
+ memoryPool[whichPool].currentNumberOfPools++;
+
+ poolNumber++;
+
+ return TRUE;
+ }
+
+ static BOOL AddPool(uint32 whichPool, uint32 poolSize)
+ {
+ if (memoryPool[whichPool].poolInfo[0].pMemory == NULL)
+ return FALSE;
+
+ return CreatePool(whichPool, poolSize);
+ }
+
+ static void* AllocateFromPsos(uint32 whichPool, uint32 poolIndex, uint32 size)
+ {
+ uint32 retval;
+ void* pMemory;
+
+ PORT_ASSERT(memoryPool[whichPool].poolInfo[poolIndex].poolId);
+
+ retval = pt_getbuf(memoryPool[whichPool].poolInfo[poolIndex].poolId, &pMemory);
+
+ /* If we got memory, then return */
+ if (retval == 0)
+ {
+ PORT_ASSERT_GOOD_WRITE_POINTER(pMemory);
+ *((unsigned long *)pMemory) = (whichPool << 27) + (poolIndex << 24) + size;
+ return (unsigned long *)pMemory + 1;
+ }
+ else
+ return NULL;
+ }
+
+ static void* SearchPoolsForMemory(uint32 whichPool, uint32 size)
+ {
+ void* pMemory;
+ uint32 poolIndex;
+ /* Get memory from main region */
+ if (whichPool == 0)
+ {
+ pMemory = malloc(size);
+
+ /* No memory protection */
+ if (pMemory == NULL)
+ {
+ /* No memory left in system */
+ return NULL;
+ }
+
+ PORT_ASSERT_GOOD_WRITE_POINTER(pMemory);
+ *((unsigned long *)pMemory) = (whichPool << 27) + size;
+ return (unsigned long *)pMemory + 1;
+ }
+
+ /* Allocate memory from the first available bin (partition) */
+ for (poolIndex = 0; poolIndex < memoryPool[whichPool].currentNumberOfPools; poolIndex++)
+ {
+ pMemory = AllocateFromPsos(whichPool, poolIndex, size);
+ if (pMemory != NULL)
+ return pMemory;
+ }
+
+ /* Made it here because we ran out of memory in the pool, so try to add more pools */
+ if (AddPool(whichPool, memoryPool[whichPool].poolInfo[0].size >> 1) == FALSE)
+ {
+ /* All pools of this size have been consumed */
+ return NULL;
+ }
+
+ /* Allocate memory from newly created pool */
+ pMemory = AllocateFromPsos(whichPool, memoryPool[whichPool].currentNumberOfPools - 1, size);
+ if (pMemory != NULL)
+ return pMemory;
+
+ /* If we can't allocate from the newly created pool, then we have problems */
+ /* No memory protection */
+
+ /* No memory left in system */
+ return NULL;
+ }
+
+ void* PortMemBlockAllocateFromPool(uint32 size)
+ {
+ void* pMemory = NULL;
+ int poolIndex;
+ BOOL foundPool = FALSE;
+ uint32 whichPool;
+
+ PORT_ASSERT((size & 0xff000000) == 0);
+
+ size += 4;
+ whichPool = findBin(size); /* Add 4 so I can store info with data */
+ MemoryTrackingAdd(size);
+
+ /* If pool exists for the size needed, then use it, else find next largest pool */
+ for (poolIndex = whichPool; poolIndex < 32; poolIndex++)
+ if (memoryPool[poolIndex].poolInfo[0].pMemory != NULL)
+ {
+ foundPool = TRUE;
+ whichPool = poolIndex;
+ break;
+ }
+
+ /* If next largest pool doesn't exist, then use pool 0 (regions) */
+ if (!foundPool)
+ whichPool = 0;
+
+ /* Allocate memory from the first available bin */
+ pMemory = SearchPoolsForMemory(whichPool, size);
+ PORT_ASSERT_GOOD_WRITE_POINTER(pMemory);
+ return pMemory;
+ }
+
+ void PortMemBlockDeleteFromPool(void* pMemory)
+ {
+ unsigned long *pRealMemory = (unsigned long *)pMemory - 1;
+
+ uint32 whichPool = (*pRealMemory >> 27) & 0x0000001f;
+ uint32 whichBin = (*pRealMemory >> 24) & 0x00000007;
+
+ PORT_ASSERT_GOOD_WRITE_POINTER(pMemory);
+ MemoryTrackingDelete(*pRealMemory & 0x00ffffff);
+
+
+ if (whichPool == 0)
+ {
+ free(pRealMemory);
+ }
+ else
+ {
+ uint32 retval = pt_retbuf(memoryPool[whichPool].poolInfo[whichBin].poolId, pRealMemory);
+ PORT_ASSERT(retval == 0);
+ }
+ }
+
+ /* PortMemGetPoolSize() : return size of portable memory pool, or 0 if
+ * unknown.
+ */
+ int PortMemBlockGetPoolSize(void)
+ {
+ return 0; /* TODO: Find size of pool: 4Mar02 */
+ }
+
+ /* PortMemBlockSetPoolSize() : set size of portable memory pool on PSOS.
+ * This must be called before PortMemoryInit(), which is called by PortInit().
+ */
+ void PortMemBlockSetPoolSize(size_t sizeInBytes)
+ {}
+
+ int PortMemBlockInit(void)
+ {
+ InitPools();
+ CreatePool(findBin(1 << 3), 3000);
+ CreatePool(findBin(1 << 4), 10000);
+ CreatePool(findBin(1 << 5), 8000);
+ CreatePool(findBin(1 << 6), 16000);
+ CreatePool(findBin(1 << 7), 5000);
+ CreatePool(findBin(1 << 8), 1000);
+ CreatePool(findBin(1 << 9), 2000);
+ CreatePool(findBin(1 << 10), 50);
+ CreatePool(findBin(1 << 11), 20);
+ CreatePool(findBin(1 << 12), 24);
+ CreatePool(findBin(1 << 13), 16);
+ CreatePool(findBin(1 << 14), 10);
+ CreatePool(findBin(1 << 15), 16);
+ CreatePool(findBin(1 << 16), 4);
+ CreatePool(findBin(1 << 18), 6);
+
+ MemoryTrackingInit();
+ }
+
+ void PortMemBlockTerm(void)
+ {
+ TermPools();
+ }
+
+ void PortMemBlockTrackDump(void)
+ {
+ int i;
+
+ printf("\nCurrent Memory Usage = %d\nMaximum Memory Usage = %d\nTotal Memory Allocation = %d\n\n",
+ gMemoryTracking.sCurrentAllocationSize, gMemoryTracking.sMaximumAllocationSize, gMemoryTracking.sTotalAllocationSize);
+
+ printf("\nCurrent Real Memory Usage = %d\nMaximum Real Memory Usage = %d\nTotal Real Memory Allocation = %d\n\n",
+ gMemoryTracking.sCurrentAllocRealSize, gMemoryTracking.sMaximumAllocRealSize, gMemoryTracking.sTotalAllocRealSize);
+
+ for (i = 0; i < NUM_TRACKING_BINS; i++)
+ printf("Max size of 2^%2d byte objects = %d\n", i, gMemoryTracking.sMaximumAllocationSizeArray[i]);
+
+ printf("\nCurrent Memory Objects = %d\nMaximum Memory Objects = %d\nTotal Memory Objects = %d\n\n",
+ gMemoryTracking.sCurrentAllocationNumber, gMemoryTracking.sMaximumAllocationNumber, gMemoryTracking.sTotalAllocationNumber);
+
+ for (i = 0; i < NUM_TRACKING_BINS; i++)
+ printf("Max number for 2^%2d byte objects = %d\n", i, gMemoryTracking.sMaximumAllocationNumberArray[i]);
+ }
+
+ /* PortMemBlockGetMaxMemUsed() : return the maximum real memory allocated.
+ * There is another function of the same name in pmalloc.c, for tracking
+ * non-psos block memory.
+ */
+ int PortMemBlockGetMaxMemUsed(void)
+ {
+ return gMemoryTracking.sMaximumAllocRealSize;
+ }
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PORTABLE_MEM_MGR == PORTABLE_PSOS_BLOCK_SCHEME_MEM_MGR */
+
diff --git a/portable/src/pmemfixed.c b/portable/src/pmemfixed.c
new file mode 100644
index 0000000..4d16ac1
--- /dev/null
+++ b/portable/src/pmemfixed.c
@@ -0,0 +1,705 @@
+/*---------------------------------------------------------------------------*
+ * pmemfixed.c *
+ * *
+ * Copyright 2007, 2008 Nuance Communciations, Inc. *
+ * *
+ * 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 "pmemory.h"
+#include "plog.h"
+
+#undef malloc
+#undef calloc
+#undef realloc
+#undef free
+
+
+#ifdef PORTABLE_FIXED_SIZE_MEM_BLOCK_SCHEME
+
+/*
+ How does the Fixed Size Memory Block Manager Work?
+ The memory manager manages an unlimited number of pools, each containing a linked list
+ of free memory blocks of a fixed size. The memory pools are ordered in increasing block
+ size, eg. pool # 0 contains 4 byte memory blocks, pool # 1 contains 8, etc. Each memory
+ block consists of a header and body. The header (which is currently 8 bytes long) is used
+ to store the address of the next free memory block in the linked list, and to store the
+ memory block's pool ID (this is used by the free function to determine which pool the block
+ originated from). The body is simply the usable memory. Whenever the application requests
+ memory of a given size, the memory manager selects the appropriate memory pool which contain
+ blocks large enough to satisfy the request. The memory manager removes a block from the
+ linked list and returns the address of the memory block body. If there are no blocks
+ available in the pool, then more blocks are created (if there is memory available); the
+ number created is configurable. If it is not possible to create more blocks, then the
+ memory manager searches the remaining pools in the sequence until it finds a free block or
+ it runs out of pools (in this case it will return a null pointer to the calling code).
+
+ How is the memory space allocated to the fixed block pools?
+ At start-up the memory manager requests one large memory block from the system (the size is
+ defined by #define MEM_SIZE). This memory is used to a) create the fixed size memory pools
+ (each contain the initial number defined in the code) and b) to create extra memory blocks
+ each time a particular pool has been exhausted (the number created is configurable for each
+ memory pool also). Once all of this memory has been used up it is also possible to make
+ further requests to the system for more memory (to create more fixed memory blocks); this
+ feature is switched on using the compilation flag ALLOW_ADDITIONAL_SYS_MEM_ALLOCS. Note
+ that once memory blocks have been added to a memory pool they cannot be removed and reused
+ in another, eg a 0.5 MByte memory block could not be removed from its 0.5 Mbyte pool in
+ order to create smaller 4 byte blocks in the 4byte block pool.
+
+ How is the large memory block from the system allocated?
+ It can be allocated in one of three ways depending on compile time definitions. If you define
+ STATIC_MEMORY_POOL, it's allocated as a static array. If you define RTXC_PARTITION_MEMORY,
+ it's allocated from the HEAP_MAP memory partition. If you don't define anything, it's allocated
+ using the system malloc call. Of course, RTXC_PARTITION should only be defined on when you building
+ for the RTXC operating system.
+
+ If STATIC_MEMORY_POOL or RTXC_PARTITION is defined, you cannot define ALLOW_ADDITIONAL_SYS_MEM_ALLOCS.
+
+ Key Points:
+ 1. Configurable memory block sizes (not restricted to power of 2 sizes).
+ 2. Best fit algorith.
+ 3. Dynamically increases the pool sizes (from an initial number).
+ 4. Can limit the total heap size.
+ 5. Configurable initial pool sizes.
+ 6. Allow additional system memory allocations in order to increase the pool sizes when the
+ 'heap' limit has been reached.
+ 7. Doesn't support block consolidation, and reuse across pools.
+
+*/
+
+/*************************** Header Files *************************************/
+
+#ifdef RTXC_PARTITION_MEMORY
+#include <rtxcapi.h>
+/* TODO - When rtxcgen is run, it will create this header file that should contain
+ * identifiers for various memory partitions that we will be using. For now, in order
+ * to get a compile, define a partition identifier.
+ */
+#define HEAP_MAP 1
+
+#endif
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+
+ /*************************** Macros Definitions *******************************/
+ /* All of these should be defined on the command line
+ * #define MEM_MGR_STATS
+ * #define ALLOW_ADDITIONAL_SYS_MEM_ALLOCS
+ * #define ALLOW_POOL_GROWTHS
+ * #define MEM_SIZE
+ */
+
+ /*
+ #if (defined(STATIC_MEMORY_POOL) || defined(RTXC_PARTITION_MEMORY)) && defined(ALLOW_ADDITIONAL_SYS_MEM_ALLOCS)
+ #error Can't allocate additional memory blocks from the system.
+ #endif
+ */
+ /* TODO: Need to figure out a good size for this. */
+ /* This had better be the same as the block in HEAP_MAP as defined when building RTXC. */
+
+#ifndef MEM_SIZE
+ /* If not defined on the command line, use a default value of 10 megabytes for the heap. */
+#define MEM_SIZE (10 * 1024 * 1024) /* 10 MBytes */
+#endif
+
+#define MEM_BLOCK_HDR 8 /* (bytes): 16 bit Pool ID, 16 bit signature, 32 bit next block pointer */
+#define MEM_POOL_ID_OFFSET 0
+#define NEXT_BLOCK_PTR_OFFSET 1 /* (no. of 4 byte words) */
+#define MEM_BLOCK_HDR_OFFSET 2 /* (no. of 4 byte words) */
+
+#define MEM_POOL_ID_MASK 0x000000FF
+#define MEM_REQ_SIZE_MASK 0xFFFFFF00
+#define MEM_REQ_SIZE_BIT_SHIFT 8
+
+
+
+ /*************************** Type Defs ****************************************/
+
+ typedef struct
+ {
+ unsigned int accReqSize;
+ unsigned int maxReqSize;
+ unsigned int allocated;
+ unsigned int accAllocated;
+ unsigned int accFreed;
+ unsigned int max_alloc;
+ }
+ MEM_STATS;
+
+
+
+ /*************************** Global Variables *********************************/
+
+ int memPoolsInitialised = 0;
+
+ unsigned int memBlockSize[] = { 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288 };
+ /*unsigned int memBlockNum[] = { 400, 1600, 17000, 8192, 13440, 512, 384, 4352, 900, 7000, 256, 2048, 1024, 128, 128, 256, 6000, 2500, 380, 170, 85, 40, 30, 120, 40, 2, 1, 2, 2 };*/
+ unsigned int memBlockNum[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ unsigned int memBlkGrowthNum[] = { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
+
+#define NUM_OF_POOLS (sizeof( memBlockSize ) / sizeof( unsigned int ))
+
+ static unsigned int memBlkGrowths[NUM_OF_POOLS];
+
+#ifdef STATIC_MEMORY_POOL
+ static char pHeap[MEM_SIZE];
+#else
+ static char *pHeap;
+#endif
+ static char* pReservedHeapMem;
+ static char* pMemPools[NUM_OF_POOLS];
+
+ static unsigned int initialHeapSize = MEM_SIZE;
+ static unsigned int usedHeapSize = 0;
+ static unsigned int reservedHeapSize = 0;
+ static unsigned int totalSystemAllocMem = 0;
+ static unsigned int numOfSystemAllocs = 0;
+
+ static MEM_STATS memStats[NUM_OF_POOLS];
+ static unsigned int allocatedMem = 0;
+ static unsigned int maxAllocMem = 0;
+
+
+
+ /*************************** Function Prototypes ******************************/
+
+ void initAllMemPools(void);
+ char* initMemPool(int poolId, int memBlockSize, int numOfMemBlocks, char** startAddress);
+ void* myMalloc(size_t size);
+ void myFree(void* ptr);
+ void displayMemStats(void);
+ void increaseMemPoolSize(unsigned int poolId);
+
+
+
+ /*************************** Function Definitions *****************************/
+
+ /*******************************************************************************
+ *
+ * Function: PortMallocInit
+ *
+ * Args: void
+ *
+ * Returns: void
+ *
+ * Description: API function which initialises the fixed size memory pools. Can
+ * be called multiple times in a session, but is only effective the
+ * first time it is called.
+ *
+ *******************************************************************************/
+
+ void PortMallocInit(void)
+ {
+ if (0 == memPoolsInitialised)
+ {
+ initAllMemPools();
+ }
+ }
+
+
+
+
+
+ int PortMallocGetMaxMemUsed(void)
+ {
+ return (int)maxAllocMem;
+ }
+
+
+
+ /*******************************************************************************
+ *
+ * Function: PortMallocSetPoolSize
+ *
+ * Args: Pool size (size_t)
+ *
+ * Returns: void
+ *
+ * Description: API function used to set the initial heap size. Note this can be
+ * called at any time, but is only effective if the memory manager
+ * has not already been initialised.
+ *
+ *******************************************************************************/
+
+ void PortMallocSetPoolSize(size_t size)
+ {
+#if !defined(STATIC_MEMORY_POOL) && !defined(RTXC_PARTITION_MEMORY)
+ if (!memPoolsInitialised)
+ {
+ initialHeapSize = (unsigned int)size;
+ }
+#else
+ (void)size;
+#endif
+ }
+
+
+
+ /*******************************************************************************
+ *
+ * Function: PortMallocGetPoolSize
+ *
+ * Args: void
+ *
+ * Returns: Pool Size (int)
+ *
+ * Description: API function to return the initial heap size.
+ *
+ *******************************************************************************/
+
+ int PortMallocGetPoolSize(void)
+ {
+ return (int)initialHeapSize;
+ }
+
+
+
+ /*******************************************************************************
+ *
+ * Function: initAllMemPools
+ *
+ * Args: void
+ *
+ * Returns: void
+ *
+ * Description: Internal function which is used to initialise all of the
+ * memory pools. Note it can be called many times but is only
+ * effective the first time it is called.
+ *
+ *******************************************************************************/
+
+ void initAllMemPools(void)
+ {
+ char *availableMemStartAddress;
+
+ if (0 == memPoolsInitialised)
+ {
+ int ii;
+
+ /* Calculate the required heap size */
+ for (ii = 0; ii < NUM_OF_POOLS; ii++)
+ {
+ usedHeapSize += (memBlockSize[ii] + MEM_BLOCK_HDR) * memBlockNum[ii];
+ }
+
+ if (initialHeapSize < usedHeapSize)
+ {
+ /* Insuffucient heap memory; abort initialisation */
+ return;
+ }
+
+
+#if defined(STATIC_MEMORY_POOL)
+ /* pHead has already been allocated, statically. Don't need to do anything. */
+#elif defined(RTXC_PARTITION_MEMORY)
+ /* Grab the one and only block in HEAP_MAP. */
+ pHeap = KS_alloc(HEAP_MAP);
+ /* MEM_SIZE has better equal the size of HEAP_MAP's block. */
+ PORT_ASSERT(MEM_SIZE == KS_inqmap(HEAP_MAP));
+#else
+ /* Use the system malloc for heap allocation. */
+
+ pHeap = (char*)malloc(initialHeapSize);
+#endif
+ if (0 == pHeap)
+ {
+ /* Unable to get memory for heap; abort initialisation */
+ return;
+ }
+
+ totalSystemAllocMem = initialHeapSize;
+ numOfSystemAllocs++;
+ reservedHeapSize = initialHeapSize - usedHeapSize;
+
+ /* Initialise each memory pool */
+ availableMemStartAddress = pHeap;
+
+ for (ii = 0; ii < NUM_OF_POOLS; ii++)
+ {
+ pMemPools[ii] = 0;
+
+ if (0 != memBlockNum[ii])
+ {
+ pMemPools[ii] = initMemPool(ii, memBlockSize[ii] + MEM_BLOCK_HDR, memBlockNum[ii], &availableMemStartAddress);
+ }
+ }
+
+ pReservedHeapMem = availableMemStartAddress;
+
+ memPoolsInitialised = 1;
+ }
+ }
+
+
+
+ /*******************************************************************************
+ *
+ * Function: initMemPool
+ *
+ * Args: Pool ID (int), Memory Block Size (int), Number of Memory Blocks
+ * (int), Heap Memory Start Address (char**)
+ *
+ * Returns: Memory Pool Start Address (char*)
+ *
+ * Description: Internal function used to fill a specified memory pool with a
+ * specified number of memory blocks of a specified size. The heap
+ * memory start address is adjusted to point to the next available
+ * memory following the newly created pool.
+ *
+ *******************************************************************************/
+
+ char* initMemPool(int poolId, int memBlockSize, int numOfMemBlocks, char** startAddress)
+ {
+ char* pPrevMemBlock = 0;
+ char* pCurrMemBlock = 0;
+ char* pStartMemPool = 0;
+ int ii;
+
+ for (ii = 0; ii < numOfMemBlocks; ii++)
+ {
+ pCurrMemBlock = &((*startAddress)[ii*memBlockSize]);
+
+ *((unsigned int*)pCurrMemBlock) = poolId;
+
+ if (0 != pPrevMemBlock)
+ {
+ ((unsigned int*)pPrevMemBlock)[NEXT_BLOCK_PTR_OFFSET] = (unsigned int)pCurrMemBlock;
+ }
+
+ pPrevMemBlock = pCurrMemBlock;
+ }
+
+ ((unsigned int*)pPrevMemBlock)[NEXT_BLOCK_PTR_OFFSET] = 0;
+
+ pStartMemPool = *startAddress;
+
+ *startAddress = (*startAddress) + (ii * memBlockSize);
+
+ return pStartMemPool;
+ }
+
+
+
+ /*******************************************************************************
+ *
+ * Function: PortMalloc
+ *
+ * Args: Size (size_t)
+ *
+ * Returns: Pointer to memory block (void*)
+ *
+ * Description: API function which is used by the application to request memory.
+ * A null pointer is returned if the memory manager is unable to
+ * satisfy the request.
+ *
+ *******************************************************************************/
+
+ void* PortMalloc(size_t size)
+ {
+ int poolId;
+ char *pMemBlock;
+ int ii;
+
+ /* Make sure the memory manager has been initialised */
+ if (0 == memPoolsInitialised)
+ {
+ initAllMemPools();
+ }
+
+ poolId = NUM_OF_POOLS;
+ pMemBlock = 0;
+
+ /* Find the best fit memory block */
+ for (ii = 0; ii < NUM_OF_POOLS; ii++)
+ {
+ if (memBlockSize[ii] >= size)
+ {
+ poolId = ii;
+
+ break;
+ }
+ }
+
+ /* Ensure that the requested size is not larger than the largest block size */
+ if (NUM_OF_POOLS > poolId)
+ {
+ /* Search the selected memory pool for a memory block; if there are none
+ then try to create some more blocks. If this is not possible then
+ search the next largest memory block pool. Repeat until either a block
+ is found, or there are no pools left */
+ for (ii = poolId; ii < NUM_OF_POOLS; ii++)
+ {
+#ifdef ALLOW_POOL_GROWTHS
+ /* If there are no blocks left, try to create some more */
+ if (0 == pMemPools[ii])
+ {
+ increaseMemPoolSize(ii);
+ }
+#endif /* ALLOW_POOL_GROWTHS */
+
+ if (0 != pMemPools[ii])
+ {
+ /* Remove the memory block from the pool linked-list */
+ pMemBlock = pMemPools[ii];
+
+ pMemPools[ii] = (char*)(((unsigned int*)pMemBlock)[NEXT_BLOCK_PTR_OFFSET]);
+
+#ifdef MEM_MGR_STATS
+ /* Record the requested size in the memory block header - this is used
+ by PortFree to determine how much requested memory has been free'd */
+ *((unsigned int*)pMemBlock) = ii | (size << MEM_REQ_SIZE_BIT_SHIFT);
+#endif /* MEM_MGR_STATS */
+
+ /* Adjust the memory block pointer to point to the useful portion of the
+ memory block, ie beyond the header */
+ pMemBlock = pMemBlock + MEM_BLOCK_HDR;
+
+#ifdef MEM_MGR_STATS
+ /* Update the memory statistics */
+ allocatedMem += size;
+
+ if (allocatedMem > maxAllocMem)
+ {
+ maxAllocMem = allocatedMem;
+ }
+
+ memStats[ii].accReqSize += size;
+ memStats[ii].accAllocated++;
+ memStats[ii].allocated++;
+
+ if (memStats[ii].maxReqSize < size)
+ {
+ memStats[ii].maxReqSize = size;
+ }
+
+ if (memStats[ii].allocated > memStats[ii].max_alloc)
+ {
+ memStats[ii].max_alloc = memStats[ii].allocated;
+ }
+#endif /* MEM_MGR_STATS */
+ break;
+ }
+ }
+ }
+
+ return (void*)pMemBlock;
+ }
+
+
+#ifdef ALLOW_POOL_GROWTHS
+ /*******************************************************************************
+ *
+ * Function: increaseMemPoolSize
+ *
+ * Args: Pool ID (unsigned int)
+ *
+ * Returns: void
+ *
+ * Description: Increases the number of blocks in a given pool by the number
+ * specified in the array memBlkGrowthNum if there is memory
+ * available. Memory is allocated from the heap reserve if
+ * availabe, else it is requested from the system (if the
+ * compilation flag ALLOW_ADDITIONAL_SYS_MEM_ALLOCS is defined. If
+ * there is insufficient memory then the operation is aborted
+ * without notification to the calling code.
+ *
+ *******************************************************************************/
+
+ void increaseMemPoolSize(unsigned int poolId)
+ {
+ unsigned int requiredMemSize = memBlkGrowthNum[poolId] * (memBlockSize[poolId] + MEM_BLOCK_HDR);
+
+ /* See if there is enough heap reserve memory */
+ if (requiredMemSize <= reservedHeapSize)
+ {
+ /* We're in luck; there's enough space */
+ pMemPools[poolId] = initMemPool(poolId, memBlockSize[poolId] + MEM_BLOCK_HDR, memBlkGrowthNum[poolId], &pReservedHeapMem);
+
+ memBlockNum[poolId] += memBlkGrowthNum[poolId];
+
+ reservedHeapSize -= requiredMemSize;
+ usedHeapSize += requiredMemSize;
+
+#ifdef MEM_MGR_STATS
+ memBlkGrowths[poolId]++;
+#endif /* MEM_MGR_STATS */
+ }
+#ifdef ALLOW_ADDITIONAL_SYS_MEM_ALLOCS
+ else
+ {
+ /* There's not enough memory in the heap reserve, so request it from the system */
+ char* pStartAddress = (char*)malloc(requiredMemSize);
+
+ if (0 != pStartAddress)
+ {
+ /* The system has allocated some memory, so let's make some more blocks */
+ pMemPools[poolId] = initMemPool(poolId, memBlockSize[poolId] + MEM_BLOCK_HDR, memBlkGrowthNum[poolId], &pStartAddress);
+
+ memBlockNum[poolId] += memBlkGrowthNum[poolId];
+
+ totalSystemAllocMem += requiredMemSize;
+ numOfSystemAllocs++;
+
+#ifdef MEM_MGR_STATS
+ memBlkGrowths[poolId]++;
+#endif /* MEM_MGR_STATS */
+ }
+ }
+#endif /* ALLOW_ADDITIONAL_SYS_MEM_ALLOCS */
+ }
+#endif /* ALLOW_POOL_GROWTHS */
+
+
+
+ /*******************************************************************************
+ *
+ * Function: PortFree
+ *
+ * Args: Memory Block Pointer (void*)
+ *
+ * Returns: void
+ *
+ * Description: API function used by the application code to return a memory
+ * block to the appropriate pool. Note that this function is not
+ * able to handle null or stale memory block pointers; calling this
+ * function under these conditions will result in unpredictable
+ * behavior.
+ *
+ *******************************************************************************/
+
+ void PortFree(void* pMem)
+ {
+ unsigned int tmpVal;
+ unsigned char poolId;
+ char* pCurrentHead;
+#ifdef MEM_MGR_STATS
+ unsigned int reqMemSize;
+#endif
+
+ /* What is the memory block pool id ? */
+ tmpVal = ((unsigned int*)pMem)[-MEM_BLOCK_HDR_OFFSET+MEM_POOL_ID_OFFSET];
+ poolId = tmpVal & MEM_POOL_ID_MASK;
+
+ /* Add the memory block to the appropriate pool */
+ pCurrentHead = pMemPools[poolId];
+ ((unsigned int*)pMem)[-MEM_BLOCK_HDR_OFFSET+NEXT_BLOCK_PTR_OFFSET] = (unsigned int)pCurrentHead;
+ pMemPools[poolId] = (char*) & (((unsigned int*)pMem)[-MEM_BLOCK_HDR_OFFSET]);
+
+#ifdef MEM_MGR_STATS
+ /* What was the requested memory size ? */
+ reqMemSize = tmpVal >> MEM_REQ_SIZE_BIT_SHIFT;
+
+ allocatedMem -= reqMemSize;
+
+ PORT_ASSERT(allocatedMem >= 0);
+
+ memStats[poolId].accFreed++;
+ memStats[poolId].allocated--;
+#endif /* MEM_MGR_STATS */
+ }
+
+
+
+ /*******************************************************************************
+ *
+ * Function: displayMemStats
+ *
+ * Args: void
+ *
+ * Returns: void
+ *
+ * Description: API function used to display the overall memory and individual
+ * memory pool statistics to standard output.
+ *
+ *******************************************************************************/
+
+ void displayMemStats(void)
+ {
+ unsigned int totBNum = 0;
+ unsigned int totGrowths = 0;
+ unsigned int totAlloc = 0;
+ unsigned int totAccAlloc = 0;
+ unsigned int totAccFreed = 0;
+ unsigned int totMaxAlloc = 0;
+ unsigned int totMemWithOH = 0;
+ unsigned int totMem = 0;
+ unsigned int bytesAllocWithOH = 0;
+ unsigned int bytesAlloc = 0;
+ unsigned int maxBytesAllocWithOH = 0;
+ unsigned int maxBytesAlloc = 0;
+ unsigned int ii;
+
+ printf("\nPool ID BlkSz AvReqSz MaxReqSz NumBlk Growths Alloc AccAlloc AccFreed MaxAlloc Alloc(b) MaxA(b)\n");
+ printf("--------------------------------------------------------------------------------------------------------\n");
+
+ for (ii = 0; ii < NUM_OF_POOLS; ii++)
+ {
+ unsigned int avReqSize = 0;
+
+ if (0 != memStats[ii].accAllocated)
+ {
+ avReqSize = memStats[ii].accReqSize / memStats[ii].accAllocated;
+ }
+
+ printf(" %4i %6i %6i %6i %7i %7i %7i %7i %7i %7i %8i %8i\n", ii, memBlockSize[ii], avReqSize, memStats[ii].maxReqSize, memBlockNum[ii], memBlkGrowths[ii], memStats[ii].allocated, memStats[ii].accAllocated, memStats[ii].accFreed, memStats[ii].max_alloc, (memBlockSize[ii]*memStats[ii].allocated), (memBlockSize[ii]*memStats[ii].max_alloc));
+
+ totBNum += memBlockNum[ii];
+ totGrowths += memBlkGrowths[ii];
+ totAlloc += memStats[ii].allocated;
+ totAccAlloc += memStats[ii].accAllocated;
+ totAccFreed += memStats[ii].accFreed;
+ totMaxAlloc += memStats[ii].max_alloc;
+
+ totMemWithOH += (memBlockSize[ii] + MEM_BLOCK_HDR) * memBlockNum[ii];
+ totMem += memBlockSize[ii] * memBlockNum[ii];
+ bytesAllocWithOH += memStats[ii].allocated * (memBlockSize[ii] + MEM_BLOCK_HDR);
+ bytesAlloc += memStats[ii].allocated * memBlockSize[ii];
+ maxBytesAllocWithOH += memStats[ii].max_alloc * (memBlockSize[ii] + MEM_BLOCK_HDR);
+ maxBytesAlloc += memStats[ii].max_alloc * memBlockSize[ii];
+ }
+
+ printf("--------------------------------------------------------------------------------------------------------\n");
+ printf("Total %7i %7i %7i %7i %7i %7i %8i %8i\n\n", totBNum, totGrowths, totAlloc, totAccAlloc, totAccFreed, totMaxAlloc, bytesAlloc, maxBytesAlloc);
+ printf("Total Memory %9i bytes\n", totMemWithOH);
+ printf("Total Memory %9i bytes (without overhead)\n", totMem);
+ printf("Allocated Memory %9i bytes\n", bytesAllocWithOH);
+ printf("Allocated Memory %9i bytes (without overhead)\n", bytesAlloc);
+ printf("Max Alloc Memory %9i bytes\n", maxBytesAllocWithOH);
+ printf("Max Alloc Memory %9i bytes (without overhead)\n", maxBytesAlloc);
+ printf("\nReq Alloc Memory %9i bytes\n", allocatedMem);
+ printf("Max Rq Alloc Mem %9i bytes\n\n", maxAllocMem);
+
+ printf("Used Heap Size %9i bytes\n", usedHeapSize);
+ printf("Reserved Heap %9i bytes\n", reservedHeapSize);
+ printf("Total Sys Alloc %9i bytes\n", totalSystemAllocMem);
+ printf("Num of Sys Alloc %9i\n", numOfSystemAllocs);
+
+ printf("\n");
+ }
+
+
+
+#ifdef __cplusplus
+} /* end extern "C" */
+#endif
+
+
+#endif /* FIXED_SIZE_MEM_BLOCK_SCHEME */
diff --git a/portable/src/pmemory.c b/portable/src/pmemory.c
new file mode 100644
index 0000000..4718eab
--- /dev/null
+++ b/portable/src/pmemory.c
@@ -0,0 +1,962 @@
+/*---------------------------------------------------------------------------*
+ * pmemory.c *
+ * *
+ * Copyright 2007, 2008 Nuance Communciations, Inc. *
+ * *
+ * 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 "passert.h"
+#include "pcrc.h"
+#include "pmemory.h"
+#include "PFileSystem.h"
+#include "PStackTrace.h"
+#include "passert.h"
+#include "pmemory_ext.h"
+#include "pmutex.h"
+
+#ifndef USE_STDLIB_MALLOC
+
+#undef malloc
+#undef calloc
+#undef realloc
+#undef free
+
+static unsigned int gNbInit = 0;
+static PFile* gFile = NULL;
+static ESR_BOOL isLogEnabled = ESR_TRUE;
+
+#ifdef PMEM_MAP_TRACE
+static asr_uint32_t gMaxAlloc = -1;
+static asr_uint32_t gCurAlloc = -1;
+#endif
+
+#if defined(PORTABLE_DINKUM_MEM_MGR) || defined(PORTABLE_FIXED_SIZE_MEM_BLOCK_SCHEME)
+static size_t gMemPoolSize = (3*1024*1024); /* default value: 3M */
+#endif
+
+#ifdef USE_THREAD
+static MUTEX memMutex;
+#endif
+
+#define MAX_MEM_TAG 256
+
+/* Only PMEM_MAP_TRACE has been defined, could do other memory logging/debugging */
+#ifdef PMEM_MAP_TRACE
+
+#define PMEM_STACKTRACE 0
+/* If enabled, logs individual memory allocation, reallocation, free operations */
+#define PMEM_LOG_LOWLEVEL 0
+#elif defined(WIN32)
+#pragma message("No PMEM_MAP_TRACE")
+#endif
+
+typedef struct MemoryData_t
+{
+#ifdef PMEM_MAP_TRACE
+ int index;
+#endif
+ size_t size;
+#if PMEM_STACKTRACE
+ /**
+ * Stacktrace of where the memory was allocated from.
+ */
+ const LCHAR* stackTrace;
+ /**
+ * Pointer to next memory allocation associated with the same tag.
+ */
+ struct MemoryData_t* next;
+ /**
+ * Pointer to last memory allocation associated with the same tag.
+ */
+ struct MemoryData_t* last;
+#endif
+}
+MemoryData;
+
+#ifdef PMEM_MAP_TRACE
+typedef struct MemMapEntry_t
+{
+ /**
+ * Memory tag/ID associated with allocation.
+ */
+ const LCHAR* tag;
+ asr_uint32_t curAlloc;
+ asr_uint32_t maxAlloc;
+ unsigned int crc;
+ /**
+ * First memory allocation associated with this tag.
+ * Memory that has been deallocated will not show up on this list.
+ */
+ MemoryData* first;
+ /**
+ * Last memory allocation associated with this tag.
+ * Memory that has been deallocated will not show up on this list.
+ */
+ MemoryData* last;
+}
+MemMapEntry;
+
+static MemMapEntry gMemoryMap[MAX_MEM_TAG];
+#endif
+
+#if defined(PORTABLE_DINKUM_MEM_MGR) || defined(PORTABLE_FIXED_SIZE_MEM_BLOCK_SCHEME)
+extern ESR_ReturnCode memory_pool_creation_status; /* Verify that memory pool actually was created */
+#define malloc PortNew
+#define free PortDelete
+#endif
+
+
+#if PMEM_STACKTRACE
+static ESR_ReturnCode getStackTrace(LCHAR* stackTrace, size_t* len)
+{
+ ESR_BOOL isInit;
+ ESR_ReturnCode rc;
+
+ rc = PStackTraceIsInitialized(&isInit);
+ if (rc == ESR_SUCCESS && isInit)
+ {
+ LCHAR* index;
+ size_t bufferLen = *len;
+ size_t i;
+
+ rc = PStackTraceGetValue(stackTrace, &bufferLen);
+ if (rc == ESR_SUCCESS)
+ {
+ for (i = 0; i < 2; ++i)
+ {
+ rc = PStackTracePopLevel(stackTrace);
+ if (rc != ESR_SUCCESS)
+ {
+ pfprintf(PSTDERR, "[%s:%d] PStackTracePopLevel failed with %s\n", __FILE__, __LINE__, ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ }
+ index = stackTrace;
+ while (index)
+ {
+ index = LSTRSTR(index, L(" at\n"));
+ if (index != NULL)
+ *(index + 3) = L(' ');
+ }
+ }
+ else if (rc == ESR_NOT_SUPPORTED)
+ LSTRCPY(stackTrace, L(""));
+ else if (rc != ESR_SUCCESS)
+ {
+ pfprintf(PSTDERR, "[%s:%d] PStackTraceGetValue failed with %s\n", __FILE__, __LINE__, ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ }
+ else
+ LSTRCPY(stackTrace, L("(null)"));
+ *len = LSTRLEN(stackTrace);
+ return ESR_SUCCESS;
+CLEANUP:
+ return rc;
+}
+#endif /* PMEM_STACKTRACE */
+
+#ifdef PMEM_MAP_TRACE
+static int getIndex(const LCHAR *key)
+{
+ unsigned int crc = ~pcrcComputeString(key);
+ int initialIdx = (int)(crc % MAX_MEM_TAG);
+ int idx = initialIdx;
+
+ for (;;)
+ {
+ if (gMemoryMap[idx].tag == NULL)
+ {
+ /* found an empty slot, use it. */
+ gMemoryMap[idx].tag = key;
+ gMemoryMap[idx].curAlloc = 0;
+ gMemoryMap[idx].maxAlloc = 0;
+ gMemoryMap[idx].crc = crc;
+ gMemoryMap[idx].first = NULL;
+ gMemoryMap[idx].last = NULL;
+#if PMEM_LOG_LOWLEVEL
+ if (gFile != NULL)
+ pfprintf(gFile, L("pmem|newtag|%s|%d|\n"), key, idx);
+#endif
+ return idx;
+ }
+
+ if (gMemoryMap[idx].crc == crc &&
+ LSTRCMP(gMemoryMap[idx].tag, key) == 0)
+ {
+ /* found a matching slot, return it */
+ return idx;
+ }
+
+ if (++idx == MAX_MEM_TAG)
+ {
+ /* Look at next slot and wrap around. */
+ idx = 0;
+ }
+ if (idx == initialIdx)
+ return -1;
+ }
+}
+#endif
+
+/* Not thread-safe. But do not expect user calls this function on different threads simultaneously */
+ESR_ReturnCode PMemorySetPoolSize(size_t size)
+{
+#if defined(PORTABLE_DINKUM_MEM_MGR) || defined(PORTABLE_FIXED_SIZE_MEM_BLOCK_SCHEME)
+ if (gNbInit > 0)
+ return ESR_INVALID_STATE;
+
+ gMemPoolSize = size;
+ return ESR_SUCCESS;
+#else
+ return ESR_NOT_SUPPORTED;
+#endif
+}
+
+ESR_ReturnCode PMemoryGetPoolSize(size_t *size)
+{
+#if defined(PORTABLE_DINKUM_MEM_MGR) || defined(PORTABLE_FIXED_SIZE_MEM_BLOCK_SCHEME)
+ *size = gMemPoolSize;
+ return ESR_SUCCESS;
+#else
+ return ESR_NOT_SUPPORTED;
+#endif
+}
+
+/* it is not thread safe: hard to protect the createMutex()
+ * could fix it by using static mutex initialization in some OS,
+ * but does not work with our own pthread implementation for vxworks
+ * SUPPOSE the user just calls this function once
+ */
+ESR_ReturnCode PMemInit(void)
+{
+ ESR_ReturnCode init_status;
+
+ if (gNbInit > 0)
+ return ESR_INVALID_STATE;
+
+ init_status = createMutex(&memMutex, ESR_FALSE);
+
+ if (init_status == ESR_SUCCESS)
+ {
+ ++gNbInit;
+#ifdef PMEM_MAP_TRACE
+ memset(gMemoryMap, 0, sizeof(gMemoryMap));
+#endif
+#if defined(PORTABLE_DINKUM_MEM_MGR) || defined(PORTABLE_FIXED_SIZE_MEM_BLOCK_SCHEME)
+ PortMemSetPoolSize(gMemPoolSize);
+ PortMemoryInit();
+ /* There is no friggin' way to pass the status of the memory initialization, because of the damn macros and all the other crap */
+ /* So I am checking the value of an external variable, this sucks, but I can't ignore something this important */
+ if (memory_pool_creation_status == ESR_SUCCESS)
+ {
+ /* Reset this because with all the layers of crap, I can't guarantee we'll get to the bottom layer on a re-init */
+ memory_pool_creation_status = ESR_FATAL_ERROR;
+ }
+ else
+ {
+ pfprintf(PSTDERR, L("ESR_INVALID_STATE: Memory Pool Could Not Be Created\n"));
+ PortMemoryTerm();
+ unlockMutex(&memMutex);
+ deleteMutex(&memMutex);
+ init_status = ESR_INVALID_STATE;
+ }
+#endif
+ }
+ else
+ {
+ deleteMutex(&memMutex);
+ }
+
+#ifdef PMEM_MAP_TRACE
+ // Initialize global static variables
+ gCurAlloc = 0;
+ gMaxAlloc = 0;
+#endif
+
+ return (init_status);
+}
+
+/* it is not thread safe: hard to protect the deleteMutex()
+ * could fix it by using static mutex initialization in some OS,
+ * but does not work with our own pthread implementation for vxworks
+ * SUPPOSE the user just calls this function once
+ */
+ESR_ReturnCode PMemShutdown(void)
+{
+#ifdef PMEM_MAP_TRACE
+ size_t i;
+#endif
+
+ if (gNbInit == 0)
+ return ESR_INVALID_STATE;
+ if (gNbInit == 1)
+ {
+#ifdef PMEM_MAP_TRACE
+ for (i = 0; i < MAX_MEM_TAG; ++i)
+ {
+ free((LCHAR*) gMemoryMap[i].tag);
+ gMemoryMap[i].tag = NULL;
+ }
+#endif
+#if defined(PORTABLE_DINKUM_MEM_MGR) || defined(PORTABLE_FIXED_SIZE_MEM_BLOCK_SCHEME)
+ PortMemoryTerm();
+#endif
+ deleteMutex(&memMutex);
+ }
+ gNbInit--;
+
+ return ESR_SUCCESS;
+}
+
+ESR_ReturnCode PMemSetLogFile(PFile* file)
+{
+ if (gNbInit == 0)
+ return ESR_INVALID_STATE;
+
+ lockMutex(&memMutex);
+ gFile = file;
+ unlockMutex(&memMutex);
+
+ return ESR_SUCCESS;
+}
+
+ESR_ReturnCode PMemDumpLogFile(void)
+{
+ ESR_ReturnCode rc;
+
+ if (gNbInit == 0)
+ return ESR_INVALID_STATE;
+
+ lockMutex(&memMutex);
+ if (gFile != NULL)
+ {
+ /* Hide gFile from memory report */
+/* CHK(rc, gFile->hideMemoryAllocation(gFile));*/
+
+ rc = PMemReport(gFile);
+ if (rc != ESR_SUCCESS)
+ {
+ pfprintf(PSTDERR, L("%s: PMemDumpLogFile() at %s:%d"), ESR_rc2str(rc), __FILE__, __LINE__);
+ goto CLEANUP;
+ }
+ if (gFile != PSTDIN && gFile != PSTDOUT && gFile != PSTDERR)
+ {
+/* rc = gFile->destroy(gFile);
+ if (rc != ESR_SUCCESS)
+ {
+ pfprintf(PSTDERR, L("%s: PMemDumpLogFile() at %s:%d"), ESR_rc2str(rc), __FILE__, __LINE__);
+ goto CLEANUP;
+ }*/
+ pfclose ( gFile );
+ }
+ gFile = NULL;
+ }
+ unlockMutex(&memMutex);
+ return ESR_SUCCESS;
+CLEANUP:
+ unlockMutex(&memMutex);
+ return rc;
+}
+
+ESR_ReturnCode PMemSetLogEnabled(ESR_BOOL value)
+{
+ lockMutex(&memMutex);
+ isLogEnabled = value;
+ unlockMutex(&memMutex);
+
+ return ESR_SUCCESS;
+}
+
+ESR_ReturnCode PMemLogFree(void* ptr)
+{
+ MemoryData* data;
+#ifdef PMEM_MAP_TRACE
+ MemMapEntry* e;
+#endif
+#if PMEM_STACKTRACE && PMEM_LOG_LOWLEVEL
+ ESR_ReturnCode rc;
+#endif
+
+ if (ptr == NULL || gNbInit == 0)
+ return ESR_SUCCESS;
+
+ lockMutex(&memMutex);
+
+ data = (MemoryData*)(((unsigned char*) ptr) - sizeof(MemoryData));
+#ifdef PMEM_MAP_TRACE
+ e = gMemoryMap + data->index;
+ passert(data->index >= 0 && data->index <= MAX_MEM_TAG);
+ if (isLogEnabled)
+ {
+ passert(e->curAlloc >= data->size);
+ e->curAlloc -= data->size;
+
+ passert(gCurAlloc >= data->size);
+ gCurAlloc -= data->size;
+
+ data->size = 0;
+ }
+#if PMEM_STACKTRACE
+ if (e->first != NULL && e->first == data)
+ e->first = data->next;
+ if (e->last != NULL && e->last == data)
+ e->last = data->last;
+ if (data->last != NULL)
+ data->last->next = data->next;
+ if (data->next != NULL)
+ {
+ data->next->last = data->last;
+ data->next = NULL;
+ }
+ data->last = NULL;
+#endif
+#if PMEM_LOG_LOWLEVEL
+ if (gFile != NULL && isLogEnabled)
+ {
+#if PMEM_STACKTRACE
+ LCHAR stackTrace[P_MAX_STACKTRACE];
+ size_t len = P_MAX_STACKTRACE;
+
+ rc = getStackTrace(stackTrace, &len);
+ if (rc != ESR_SUCCESS)
+ {
+ pfprintf(PSTDERR, "[%s:%d] getStackTrace failed with %s\n", __FILE__, __LINE__, ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ pfprintf(gFile, L("pmem|free|%s|%s|%d|0x%x|%s|\n"), e->tag, data->stackTrace, data->size, ptr, stackTrace);
+#else
+ pfprintf(gFile, L("pmem|free|%s|%d|0x%x\n"), e->tag, data->size, ptr);
+#endif /* PMEM_STACKTRACE */
+ }
+#endif /* PMEM_LOG_LOWLEVEL */
+#endif /* PMEM_MAP_TRACE */
+
+ unlockMutex(&memMutex);
+ return ESR_SUCCESS;
+#if PMEM_STACKTRACE && PMEM_LOG_LOWLEVEL
+CLEANUP:
+ unlockMutex(&memMutex);
+ return rc;
+#endif
+}
+
+ESR_ReturnCode PMemReport(PFile* file)
+{
+#define TAG_SIZE 52
+#ifdef PMEM_MAP_TRACE
+ asr_uint32_t totalAlloc = 0;
+ size_t i;
+ MemMapEntry* e;
+ unsigned int crc;
+ LCHAR truncatedTag[TAG_SIZE];
+ size_t len;
+ LCHAR TAG_PREFIX[] = L("...");
+ const size_t TAG_PREFIX_SIZE = LSTRLEN(TAG_PREFIX);
+ const size_t countToCopy = (TAG_SIZE - 1) - TAG_PREFIX_SIZE;
+#endif
+#if PMEM_STACKTRACE
+ MemoryData* data;
+#endif
+
+ if (gNbInit == 0)
+ return ESR_INVALID_STATE;
+ if (file == NULL)
+ {
+ file = gFile;
+ if (file == NULL)
+ return ESR_SUCCESS;
+ }
+
+ lockMutex(&memMutex);
+#ifdef PMEM_MAP_TRACE
+ if (gFile != NULL)
+ {
+ for (i = 0, e = gMemoryMap; i < MAX_MEM_TAG; ++i, ++e)
+ {
+ if (e->tag == NULL)
+ continue;
+ crc = ~pcrcComputeString(e->tag);
+ if (crc != e->crc)
+ pfprintf(gFile, L("pmem|-|0|corrupt|%d|\n"), i);
+ }
+ }
+
+ pfprintf(file, L("%-52s %10s %15s\n"), L("Memory tag"), L("Cur. Alloc"), L("Max. Alloc"));
+
+ for (i = 0, e = gMemoryMap; i < MAX_MEM_TAG; ++i, ++e)
+ {
+ if (e->tag == NULL)
+ continue;
+ crc = ~pcrcComputeString(e->tag);
+ if (crc != e->crc)
+ pfprintf(file, L("**********%04d********** %38u %15u\n"), i, e->curAlloc, e->maxAlloc);
+ else
+ {
+ len = LSTRLEN(e->tag);
+
+ if (len > TAG_SIZE - 1)
+ {
+ LSTRCPY(truncatedTag, TAG_PREFIX);
+ LSTRCPY(truncatedTag + TAG_PREFIX_SIZE, e->tag + (len - countToCopy));
+ passert(LSTRLEN(truncatedTag) == TAG_SIZE - 1);
+ }
+ else
+ LSTRCPY(truncatedTag, e->tag);
+ pfprintf(file, L("%-52s %10u %15u\n"), truncatedTag, e->curAlloc, e->maxAlloc);
+ }
+#if PMEM_STACKTRACE
+ data = gMemoryMap[i].first;
+ while (data)
+ {
+ if (data->size != 0 && data->stackTrace != NULL)
+ {
+ LCHAR stackTrace[P_MAX_STACKTRACE];
+ LCHAR* index;
+
+ LSTRCPY(stackTrace, data->stackTrace);
+ index = stackTrace;
+ while (index)
+ {
+ index = LSTRSTR(index, L(" at "));
+ if (index != NULL)
+ *(index + 3) = L('\n');
+ }
+ pfprintf(file, L("StackTrace:\n%s\n\n"), stackTrace);
+ }
+ data = data->next;
+ }
+#endif
+ passert(e->curAlloc >= 0);
+ totalAlloc += e->curAlloc;
+ }
+ pfprintf(file, L("%-52s %10u %15u\n"), L("Total"), totalAlloc, gMaxAlloc);
+ passert(totalAlloc == gCurAlloc);
+#else
+ /* not support */
+#endif /* PMEM_MAP_TRACE */
+ unlockMutex(&memMutex);
+
+ return ESR_SUCCESS;
+}
+/*
+DESCRIPTION
+ The functionality described on this reference page is aligned with the ISO C standard. Any conflict between the requirements described here and the ISO C standard is unintentional. This volume of IEEE Std 1003.1-2001 defers to the ISO C standard.
+The malloc() function shall allocate unused space for an object whose size in bytes is specified by size and whose value is unspecified.
+
+The order and contiguity of storage allocated by successive calls to malloc() is unspecified. The pointer returned if the allocation succeeds shall be suitably aligned so that it may be assigned to a pointer to any type of object and then used to access such an object in the space allocated (until the space is explicitly freed or reallocated). Each such allocation shall yield a pointer to an object disjoint from any other object. The pointer returned points to the start (lowest byte address) of the allocated space. If the space cannot be allocated, a null pointer shall be returned. If the size of the space requested is 0, the behavior is implementation-defined: the value returned shall be either a null pointer or a unique pointer.
+
+RETURN VALUE
+Upon successful completion with size not equal to 0, malloc() shall return a pointer to the allocated space. If size is 0, either a null pointer or a unique pointer that can be successfully passed to free() shall be returned. Otherwise, it shall return a null pointer and set errno to indicate the error.
+*/
+#ifdef PMEM_MAP_TRACE
+void *pmalloc(size_t nbBytes, const LCHAR* tag, const LCHAR* file, int line)
+#else
+void *pmalloc(size_t nbBytes)
+#endif
+{
+ MemoryData* data;
+ void* result = NULL;
+ size_t actualSize;
+#ifdef PMEM_MAP_TRACE
+ int idx;
+ MemMapEntry* e;
+#endif
+#if PMEM_STACKTRACE
+ size_t stackTraceSize = P_MAX_STACKTRACE;
+ LCHAR* stackTrace;
+ ESR_BOOL isInit;
+ ESR_ReturnCode rc;
+#endif
+
+ if (gNbInit == 0)
+ return NULL;
+
+ lockMutex(&memMutex);
+
+#ifdef PMEM_MAP_TRACE
+ if (tag == NULL)
+ tag = file;
+ passert(tag != NULL);
+
+ idx = getIndex(tag);
+ if (idx == -1)
+ {
+ pfprintf(PSTDERR, L("ESR_INVALID_STATE: pmalloc() ran out of slots"));
+ goto CLEANUP;
+ }
+ if (gMemoryMap[idx].tag == tag)
+ {
+ /* This is a new key, allocate memory for it */
+ gMemoryMap[idx].tag = malloc(sizeof(LCHAR) * (LSTRLEN(tag) + 1));
+ if (gMemoryMap[idx].tag == NULL)
+ goto CLEANUP;
+ LSTRCPY((LCHAR*) gMemoryMap[idx].tag, tag);
+ }
+#endif
+ actualSize = sizeof(MemoryData) + nbBytes;
+
+ data = (MemoryData *) malloc(actualSize);
+ if (data == NULL)
+ {
+ /*
+ * printf("no space when alloc %d from file %s line %d\nmem usage: %d\n",
+ * nbBytes, file, line, PortMallocGetMaxMemUsed());
+ */
+ goto CLEANUP;
+ }
+
+#ifdef PMEM_MAP_TRACE
+ data->index = idx;
+#if PMEM_STACKTRACE
+ rc = PStackTraceIsInitialized(&isInit);
+ if (rc != ESR_SUCCESS)
+ goto CLEANUP;
+ if (isInit)
+ {
+ stackTrace = malloc(sizeof(LCHAR) * (stackTraceSize + 1));
+ if (stackTrace == NULL)
+ goto CLEANUP;
+ rc = getStackTrace(stackTrace, &stackTraceSize);
+ if (rc != ESR_SUCCESS)
+ goto CLEANUP;
+ /* Shrink stackTrace buffer */
+ passert(LSTRLEN(stackTrace) < P_MAX_STACKTRACE);
+ data->stackTrace = realloc(stackTrace, sizeof(LCHAR) * (LSTRLEN(stackTrace) + 1));
+ if (data->stackTrace == NULL)
+ {
+ free(stackTrace);
+ goto CLEANUP;
+ }
+ }
+ else
+ data->stackTrace = NULL;
+#endif
+
+ e = gMemoryMap + idx;
+
+#if PMEM_STACKTRACE
+ if (e->last != NULL)
+ e->last->next = data;
+ data->last = e->last;
+ data->next = NULL;
+ e->last = data;
+ if (e->first == NULL)
+ e->first = data;
+#endif
+#endif
+
+ if (isLogEnabled)
+ {
+ data->size = actualSize;
+#ifdef PMEM_MAP_TRACE
+ e->curAlloc += actualSize;
+ if (e->maxAlloc < e->curAlloc)
+ e->maxAlloc = e->curAlloc;
+
+ gCurAlloc += actualSize;
+ if (gMaxAlloc < gCurAlloc)
+ gMaxAlloc = gCurAlloc;
+#endif
+ }
+ else
+ data->size = 0;
+
+ result = (void*)(data + 1);
+
+#if PMEM_LOG_LOWLEVEL
+ if (gFile != NULL && isLogEnabled)
+
+ if (gFile != NULL)
+ {
+#if PMEM_STACKTRACE
+ pfprintf(gFile, L("pmem|alloc|%s|%d|0x%x|%s|\n"), tag, actualSize, result, data->stackTrace);
+#else
+ pfprintf(gFile, L("pmem|alloc|%s|%d|0x%x|\n"), tag, actualSize, result);
+#endif /* PMEM_STACKTRACE */
+ }
+#endif /* PMEM_LOG_LOWLEVEL */
+
+CLEANUP:
+ unlockMutex(&memMutex);
+ return result;
+}
+
+#ifdef PMEM_MAP_TRACE
+void *pcalloc(size_t nbItems, size_t itemSize, const LCHAR* tag, const LCHAR* file, int line)
+#else
+void *pcalloc(size_t nbItems, size_t itemSize)
+#endif
+{
+ void* result = NULL;
+
+ if (gNbInit == 1)
+ {
+#ifdef PMEM_MAP_TRACE
+ result = (MemoryData *)pmalloc(nbItems * itemSize, tag, file, line);
+#else
+ result = (MemoryData *)pmalloc(nbItems * itemSize);
+#endif
+ if (result != NULL)
+ memset(result, 0, nbItems * itemSize);
+ }
+ return (result);
+}
+
+/*
+DESCRIPTION
+The realloc() function changes the size of the memory object pointed to by ptr to the size specified by size. The contents of the object will remain unchanged up to the lesser of the new and old sizes. If the new size of the memory object would require movement of the object, the space for the previous instantiation of the object is freed. If the new size is larger, the contents of the newly allocated portion of the object are unspecified. If size is 0 and ptr is not a null pointer, the object pointed to is freed. If the space cannot be allocated, the object remains unchanged.
+If ptr is a null pointer, realloc() behaves like malloc() for the specified size.
+
+If ptr does not match a pointer returned earlier by calloc(), malloc() or realloc() or if the space has previously been deallocated by a call to free() or realloc(), the behaviour is undefined.
+
+The order and contiguity of storage allocated by successive calls to realloc() is unspecified. The pointer returned if the allocation succeeds is suitably aligned so that it may be assigned to a pointer to any type of object and then used to access such an object in the space allocated (until the space is explicitly freed or reallocated). Each such allocation will yield a pointer to an object disjoint from any other object. The pointer returned points to the start (lowest byte address) of the allocated space. If the space cannot be allocated, a null pointer is returned.
+
+ RETURN VALUE
+Upon successful completion with a size not equal to 0, realloc() returns a pointer to the (possibly moved) allocated space. If size is 0, either a null pointer or a unique pointer that can be successfully passed to free() is returned. If there is not enough available memory, realloc() returns a null pointer
+*/
+#ifdef PMEM_MAP_TRACE
+void *prealloc(void *ptr, size_t newSize, const LCHAR *file, int line)
+#else
+void *prealloc(void *ptr, size_t newSize)
+#endif
+{
+ MemoryData* oldData;
+ MemoryData* newData;
+ void *result = NULL;
+ size_t actualSize;
+#ifdef PMEM_MAP_TRACE
+ MemMapEntry* e;
+#endif
+ size_t oldSize;
+#if PMEM_STACKTRACE
+ const LCHAR* oldStackTrace;
+ MemoryData* oldNext;
+ MemoryData* oldLast;
+#endif
+ ESR_BOOL bMalloc = ESR_FALSE;
+
+ if (gNbInit == 0)
+ return NULL;
+
+ if (newSize == 0 && ptr != NULL)
+ {
+#ifdef PMEM_MAP_TRACE
+ pfree(ptr, file, line);
+#else
+ pfree(ptr);
+#endif
+ return NULL;
+ }
+ else if (ptr == NULL)
+ {
+#ifdef PMEM_MAP_TRACE
+ return pmalloc(newSize, NULL, file, line);
+#else
+ return pmalloc(newSize);
+#endif
+ }
+
+ lockMutex(&memMutex);
+
+ oldData = (MemoryData *)(((unsigned char *) ptr) - sizeof(MemoryData));
+ oldSize = oldData->size;
+ passert(oldSize >= 0);
+#if PMEM_STACKTRACE
+ oldStackTrace = oldData->stackTrace;
+ oldNext = oldData->next;
+ oldLast = oldData->last;
+#endif
+#ifdef PMEM_MAP_TRACE
+ e = gMemoryMap + oldData->index;
+#endif
+
+ actualSize = newSize + sizeof(MemoryData);
+ if (oldSize != actualSize)
+ {
+#if defined(PORTABLE_DINKUM_MEM_MGR) || defined(PORTABLE_FIXED_SIZE_MEM_BLOCK_SCHEME)
+ newData = (MemoryData *) PortNew(actualSize);
+ if (newData == NULL)
+ {
+ pfprintf(PSTDERR, L("OUT_OF_MEMORY: prealloc() failed at %s:%d"), __FILE__, __LINE__);
+ return NULL;
+ }
+ bMalloc = ESR_TRUE;
+ if (oldSize >= actualSize)
+ {
+ memcpy(newData, oldData, actualSize);
+ }
+ else
+ {
+ memcpy(newData, oldData, oldSize);
+ }
+ PortDelete(oldData);
+#else
+ newData = (MemoryData *) realloc(oldData, actualSize);
+ bMalloc = ESR_TRUE;
+#endif
+ }
+ else /* No change */
+ {
+ newData = oldData;
+ }
+
+#ifdef PMEM_MAP_TRACE
+ if (newData != NULL && bMalloc)
+ {
+ if (isLogEnabled)
+ {
+ e->curAlloc += actualSize - oldSize;
+ if (e->maxAlloc < e->curAlloc)
+ e->maxAlloc = e->curAlloc;
+
+ gCurAlloc += actualSize - oldSize;
+ if (gMaxAlloc < gCurAlloc)
+ gMaxAlloc = gCurAlloc;
+ }
+
+#if PMEM_STACKTRACE
+ newData->stackTrace = oldStackTrace;
+ newData->next = oldNext;
+ newData->last = oldLast;
+ if (newData->last != NULL)
+ newData->last->next = newData;
+ if (newData->next != NULL)
+ newData->next->last = newData;
+ if (e->first == oldData)
+ e->first = newData;
+ if (e->last == oldData)
+ e->last = newData;
+#endif
+ }
+#endif
+
+ if (newData != NULL)
+ {
+ newData->size = actualSize;
+ result = (void*)(newData + 1);
+ }
+
+#if PMEM_LOG_LOWLEVEL
+ if (gFile != NULL && isLogEnabled)
+ {
+#if PMEM_STACKTRACE
+ LCHAR stackTrace[P_MAX_STACKTRACE];
+ size_t len = P_MAX_STACKTRACE;
+ ESR_ReturnCode rc;
+
+ rc = getStackTrace(stackTrace, &len);
+ if (rc != ESR_SUCCESS)
+ {
+ pfprintf(PSTDERR, "[%s:%d] getStackTrace failed with %s\n", __FILE__, __LINE__, ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ pfprintf(gFile, L("pmem|%s|%d|realloc|%d|0x%x|%s|\n"), e->tag, oldSize, actualSize, ptr, stackTrace);
+#else
+ pfprintf(gFile, L("pmem|%s|%d|realloc|%d|0x%x|\n"), e->tag, oldSize, actualSize, ptr);
+#endif /* PMEM_STACKTRACE */
+ }
+#endif /* PMEM_LOG_LOWLEVEL */
+
+ unlockMutex(&memMutex);
+ return result;
+#if PMEM_STACKTRACE && PMEM_LOG_LOWLEVEL
+CLEANUP:
+ unlockMutex(&memMutex);
+ return NULL;
+#endif
+}
+
+#ifdef PMEM_MAP_TRACE
+void pfree(void* ptr, const LCHAR* file, int line)
+#else
+void pfree(void* ptr)
+#endif
+{
+ MemoryData* data;
+#ifdef PMEM_MAP_TRACE
+ MemMapEntry* e;
+#endif
+ if (ptr == NULL || gNbInit == 0)
+ return;
+
+ lockMutex(&memMutex);
+
+ data = (MemoryData*)(((unsigned char*) ptr) - sizeof(MemoryData));
+#ifdef PMEM_MAP_TRACE
+ passert(data->index >= 0 && data->index <= MAX_MEM_TAG);
+ e = gMemoryMap + data->index;
+ if (isLogEnabled)
+ {
+ passert(e->curAlloc >= data->size);
+ e->curAlloc -= data->size;
+
+ passert(gCurAlloc >= data->size);
+ gCurAlloc -= data->size;
+ }
+#if PMEM_STACKTRACE
+ if (e->first != NULL && e->first == data)
+ e->first = data->next;
+ if (e->last != NULL && e->last == data)
+ e->last = data->last;
+ if (data->last != NULL)
+ data->last->next = data->next;
+ if (data->next != NULL)
+ {
+ data->next->last = data->last;
+ data->next = NULL;
+ }
+ data->last = NULL;
+#endif /* PMEM_STACKTRACE */
+#if PMEM_LOG_LOWLEVEL
+ if (gFile != NULL && isLogEnabled)
+ {
+#if PMEM_STACKTRACE
+ LCHAR stackTrace[P_MAX_STACKTRACE];
+ size_t len = P_MAX_STACKTRACE;
+ ESR_ReturnCode rc;
+
+ rc = getStackTrace(stackTrace, &len);
+ if (rc != ESR_SUCCESS)
+ {
+ pfprintf(PSTDERR, "[%s:%d] getStackTrace failed with %s\n", __FILE__, __LINE__, ESR_rc2str(rc));
+ goto CLEANUP;
+ }
+ pfprintf(gFile, L("pmem|free|%s|%s|%d|0x%x|%s|\n"), e->tag, data->stackTrace, data->size, ptr, stackTrace);
+#else
+ pfprintf(gFile, L("pmem|free|%s|%d|0x%x\n"), e->tag, data->size, ptr);
+#endif /* PMEM_STACKTRACE */
+ }
+#endif /* PMEM_LOG_LOWLEVEL */
+#if PMEM_STACKTRACE
+ free((LCHAR*) data->stackTrace);
+ data->stackTrace = NULL;
+#endif /* PMEM_STACKTRACE */
+#endif
+
+ free(data);
+ unlockMutex(&memMutex);
+#if PMEM_STACKTRACE && PMEM_LOG_LOWLEVEL
+CLEANUP:
+ unlockMutex(&memMutex);
+ return;
+#endif
+
+}
+
+#endif
diff --git a/portable/src/pmemory_ext.c b/portable/src/pmemory_ext.c
new file mode 100644
index 0000000..a30a6cb
--- /dev/null
+++ b/portable/src/pmemory_ext.c
@@ -0,0 +1,369 @@
+/*---------------------------------------------------------------------------*
+ * pmemory_ext.c *
+ * *
+ * Copyright 2007, 2008 Nuance Communciations, Inc. *
+ * *
+ * 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 "pmemory.h"
+#include "ptrd.h"
+#include "pmutex.h"
+#include "passert.h"
+#include "pmemory_ext.h"
+#include "pmalloc.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#if defined(USE_THREAD) && defined(USE_DINKUM_LIB_DIRECT)
+ static MUTEX memextMutex;
+#endif
+
+#ifdef RTXC
+ void* operator new(size_t size)
+ {
+ return (PortNew(size));
+ }
+ void operator delete(void* ptr)
+ {
+ PortDelete(ptr);
+ }
+#endif
+
+#if defined(PORTABLE_DINKUM_MEM_MGR) || defined(PORTABLE_FIXED_SIZE_MEM_BLOCK_SCHEME)
+
+ /* to assist with leak checking */
+static int portNewCount = 0;
+static int portDeleteCount = 0;
+
+ /* enable writing and checking of guard words if debugging is enabled */
+#ifdef _DEBUG
+ /* crash on Xanavi's board with this option on, do not know why */
+ /* #define DBG_GUARD_WORDS */
+#endif /* _DEBUG */
+
+ /* ************************************************************************************
+ * PORTABLE_PSOS_BLOCK_SCHEME_MEM_MGR || PORTABLE_DINKUM_MEM_MGR || PORTABLE_FIXED_SIZE_MEM_BLOCK_SCHEME
+ * ************************************************************************************/
+
+ /* data ******************************************************************************/
+
+ static BOOL gMemoryInitted = FALSE; /* TODO: Temporary fix to PortTerm failure */
+
+#define MEM_MGR_GetPoolSize() PortMallocGetPoolSize()
+#define MEM_MGR_SetPoolSize(sizeInBytes) PortMallocSetPoolSize(sizeInBytes)
+#define MEM_MGR_Init() PortMallocInit()
+#define MEM_MGR_Term() PortMallocTerm()
+#define MEM_MGR_Allocate(sizeInBytes) PortMalloc(sizeInBytes)
+#define MEM_MGR_Free(objectPtr) PortFree(objectPtr)
+#define MEM_MGR_Dump()
+#define MEM_MGR_GetMaxMemUsed() PortMallocGetMaxMemUsed()
+
+ /* guard word data ********************************************************/
+
+#ifdef DBG_GUARD_WORDS
+#define GUARD_BEGIN 0xbbbbbbbb
+#define GUARD_END 0xeeeeeeee
+
+#define GUARD_OFF_REQ_SIZE 0
+#define GUARD_OFF_START sizeof(unsigned int)
+#define GUARD_OFF_PTR (sizeof(unsigned int) + sizeof(unsigned int))
+#define GUARD_EXTRA (sizeof(unsigned int) + sizeof(unsigned int) + sizeof(unsigned int))
+#define GUARD_OFF_END(allocSize) ((allocSize) - sizeof(unsigned int))
+#define GUARD_ALLOC_SIZE(reqSize) ((reqSize)+GUARD_EXTRA)
+
+#define GUARD_PTR_FIELD(ptr,off) (unsigned int *)((char *)(ptr) + (off))
+#define GUARD_ALLOC_PTR(ptr) (void*) ((char *)(ptr) - GUARD_OFF_PTR)
+#endif
+
+ /* scan guard words data **************************************************/
+
+ /* maintain a static list of allocated blocks (didn't want to perform any dynamic allocation).
+ * This list can be scanned by PortMemScan() to determine if any allocated blocks
+ * have overwritten their guard words.
+ * Calling PortDelete() will check guard words upon de-allocation, but many
+ * allocated blocks are only freed at program termination, which sometimes doesn't happen.
+ *
+ * This software is enabled separately with DBG_SCAN_GUARD_WORDS, because the performance
+ * overhead is severe.
+ */
+#ifdef DBG_SCAN_GUARD_WORDS
+#define MAX_ALLOCATED_BLOCKS 80000
+ static void *allocArray[MAX_ALLOCATED_BLOCKS+1];
+ static int allocArrayCount = 0;
+
+ void AddToAllocList(void *memPtr);
+ void RemoveFromAllocList(void *memPtr);
+
+#define ADD_TO_ALLOC_LIST(ptr) AddToAllocList(ptr)
+#define REMOVE_FROM_ALLOC_LIST(ptr) RemoveFromAllocList(ptr)
+
+#else
+#define ADD_TO_ALLOC_LIST(ptr)
+#define REMOVE_FROM_ALLOC_LIST(ptr)
+#endif
+
+ /* Guard Functions ********************************************************/
+
+#ifdef DBG_SCAN_GUARD_WORDS
+ /* AddToAllocList() : maintain an array of allocated blocks that can be
+ * used by PortMemScan() to check for overwritten guard words.
+ */
+ void AddToAllocList(void *memPtr)
+ {
+ allocArray[allocArrayCount] = memPtr;
+ allocArrayCount++;
+ if (allocArrayCount >= MAX_ALLOCATED_BLOCKS)
+ {
+ char buf[256];
+ sprintf(buf, "AddToAllocList ERROR : MAX_ALLOCATED_BLOCKS is too small (%d)", allocArrayCount);
+ PORT_INTERNAL_ERROR(buf);
+ }
+ }
+
+ /* RemoveFromAllocList() : maintain an array of allocated blocks that can be
+ * used by PortMemScan() to check for overwritten guard words.
+ */
+ void RemoveFromAllocList(void *memPtr)
+ {
+ int i; /* loop index */
+ int j; /* loop index */
+ int inList = FALSE; /* TRUE when found in list */
+
+ for (i = 0; i < allocArrayCount; i++)
+ {
+ if (allocArray[i] == memPtr)
+ {
+ inList = TRUE;
+ break;
+ }
+ }
+ PORT_ASSERT(inList == TRUE); /* MUST be in list */
+ /* remove by sliding down all following entries */
+ for (j = i + 1; j < allocArrayCount; j++)
+ allocArray[j-1] = allocArray[j];
+ allocArrayCount--;
+ allocArray[allocArrayCount] = NULL; /* clear out end of list */
+ }
+
+ /* PortMemScan() : scan the array of allocated blocks, confirming that no
+ * allocated block has overwritten its guard words.
+ */
+ void PortMemScan(void)
+ {
+ int i;
+
+ PortCriticalSectionEnter(&PortMemoryCriticalSection);
+
+ /* scan the allocated memory list */
+ for (i = 0; i < allocArrayCount; i++)
+ {
+ /* verify that guard words have not been corrupted */
+ void *memPtr = allocArray[i];
+ void *allocPtr = GUARD_ALLOC_PTR(memPtr);
+ unsigned int *requestedSizePtr = GUARD_PTR_FIELD(allocPtr, GUARD_OFF_REQ_SIZE);
+ unsigned int *guardStartPtr = GUARD_PTR_FIELD(allocPtr, GUARD_OFF_START);
+ unsigned int *guardEndPtr = GUARD_PTR_FIELD(allocPtr, GUARD_OFF_END(GUARD_ALLOC_SIZE(*requestedSizePtr)));
+
+ if ((*guardStartPtr) != GUARD_BEGIN)
+ {
+ PLogError("PortMemScan : corrupted start guard from block 0x%08x \n", (int)memPtr);
+ }
+ if ((*guardEndPtr) != GUARD_END)
+ {
+ PLogError("PortMemScan : corrupted end guard from block 0x%08x \n", (int)memPtr);
+ }
+ }
+
+ PortCriticalSectionLeave(&PortMemoryCriticalSection);
+ }
+#endif /* DBG_SCAN_GUARD_WORDS */
+
+ /* Port Memory Functions ******************************************************/
+
+ /* PortMemGetPoolSize() : return size of portable memory pool, or 0 if
+ * unknown.
+ */
+ int PortMemGetPoolSize(void)
+ {
+ return MEM_MGR_GetPoolSize();
+ }
+
+ /* PortMemSetPoolSize() : set size of portable memory pool on PSOS.
+ * This must be called before PortMemoryInit(), which is called by PortInit().
+ */
+ void PortMemSetPoolSize(size_t sizeInBytes)
+ {
+ MEM_MGR_SetPoolSize(sizeInBytes);
+ }
+
+ /* PortMemoryInit() :
+ */
+
+ int PortMemoryInit(void)
+ {
+#if defined(USE_THREAD) && defined(USE_DINKUM_LIB_DIRECT)
+ if (createMutex(&memextMutex) == ESR_SUCCESS)
+#endif
+ {
+ if (!gMemoryInitted)
+ {
+ MEM_MGR_Init();
+ gMemoryInitted = TRUE;
+ }
+ }
+
+ return gMemoryInitted;
+ }
+
+ /* PortMemoryTerm() :
+ */
+
+ void PortMemoryTerm(void)
+ {
+ /* TODO: MEM_PSOS_BLOCK_SCHEME
+ * Figure out why free memory causes rn#0 is get messed up! */
+ MEM_MGR_Term();
+#if defined(USE_THREAD) && defined(USE_DINKUM_LIB_DIRECT)
+ deleteMutex(&memextMutex);
+#endif
+ gMemoryInitted = FALSE;
+ }
+
+ /* PortNew() :
+ */
+
+ void* PortNew(size_t sizeInBytes)
+ {
+ if (gMemoryInitted)
+ {
+ void *pMemory = NULL;
+
+#if defined(USE_THREAD) && defined(USE_DINKUM_LIB_DIRECT)
+ lockMutex(&memextMutex);
+#endif
+ portNewCount++;
+
+#ifdef DBG_GUARD_WORDS
+ sizeInBytes += GUARD_EXTRA; /* space for: requestedSize,guardStart,guardEnd */
+#endif
+
+ pMemory = MEM_MGR_Allocate(sizeInBytes);
+
+#ifdef DBG_GUARD_WORDS
+ if (NULL != pMemory)
+ {
+ /* at the beginning of the buffer, store the requested size and a guard word.
+ * Store another guard word at the end of the buffer.
+ */
+ /* set guard words at either end of allocated buffer; will be checked at delete time */
+ unsigned int * requestedSizePtr = GUARD_PTR_FIELD(pMemory, GUARD_OFF_REQ_SIZE);
+ unsigned int * guardStartPtr = GUARD_PTR_FIELD(pMemory, GUARD_OFF_START);
+ unsigned int * guardEndPtr = GUARD_PTR_FIELD(pMemory, GUARD_OFF_END(sizeInBytes));
+
+ *requestedSizePtr = sizeInBytes - GUARD_EXTRA;
+ *guardStartPtr = GUARD_BEGIN;
+ *guardEndPtr = GUARD_END;
+ pMemory = (void *) GUARD_PTR_FIELD(pMemory, GUARD_OFF_PTR);
+ ADD_TO_ALLOC_LIST(pMemory);
+ }
+#endif /* DBG_GUARD_WORDS */
+#if defined(USE_THREAD) && defined(USE_DINKUM_LIB_DIRECT)
+ unlockMutex(&memextMutex);
+#endif
+ return pMemory;
+ }
+#ifdef PSOSIM
+ /* PSOSIM's license manager calls new() before PSOS is running */
+ else
+ {
+ return(malloc(sizeInBytes));
+ }
+#else /* PSOSIM */
+ /* Memory allocator not initialized when request for memory was made */
+ passert(FALSE && "Call PortInit() before calling any portable functions\r\n");
+ return NULL;
+#endif /* PSOSIM */
+ }
+
+ void PortDelete(void* objectPtr)
+ {
+#if defined(USE_THREAD) && defined(USE_DINKUM_LIB_DIRECT)
+ lockMutex(&memextMutex);
+#endif
+ portDeleteCount++;
+
+#ifdef DBG_GUARD_WORDS
+ {
+ /* verify that guard words have not been corrupted */
+ void *allocPtr = GUARD_ALLOC_PTR(objectPtr);
+ unsigned int *requestedSizePtr = GUARD_PTR_FIELD(allocPtr, GUARD_OFF_REQ_SIZE);
+ unsigned int *guardStartPtr = GUARD_PTR_FIELD(allocPtr, GUARD_OFF_START);
+ unsigned int *guardEndPtr = GUARD_PTR_FIELD(allocPtr, GUARD_OFF_END(GUARD_ALLOC_SIZE(*requestedSizePtr)));
+
+ passert((*guardStartPtr) == GUARD_BEGIN);
+ passert((*guardEndPtr) == GUARD_END);
+ REMOVE_FROM_ALLOC_LIST(allocPtr);
+ objectPtr = allocPtr;
+ }
+#endif
+
+ MEM_MGR_Free(objectPtr);
+#if defined(USE_THREAD) && defined(USE_DINKUM_LIB_DIRECT)
+ unlockMutex(&memextMutex);
+#endif
+ }
+
+ void PortMemTrackDump(void)
+ {
+ MEM_MGR_Dump();
+ }
+
+ /* PortGetMaxMemUsed() : return the maximum real memory allocated.
+ * There is another function of the same name in pmalloc.c, for tracking
+ * non-psos block memory. It uses #ifndef MEM_PSOS_BLOCK_SCHEME to enable.
+ */
+ int PortGetMaxMemUsed(void)
+ {
+ return MEM_MGR_GetMaxMemUsed();
+ }
+
+ /* PortMemCntReset() : reset the New/Delete count.
+ * This is useful for checking that each new has a corresponding delete once
+ * the system gets into a steady state.
+ */
+ void PortMemCntReset()
+ {
+ portNewCount = 0;
+ portDeleteCount = 0;
+ }
+
+
+ /* PortMemGetCount() : return the accumulated new & delete counts */
+ void PortMemGetCount(int *newCount, int *deleteCount)
+ {
+ *newCount = portNewCount;
+ *deleteCount = portDeleteCount;
+ }
+
+#endif /* (==PORTABLE_PSOS_BLOCK_SCHEME_MEM_MGR) || (==PORTABLE_DINKUM_MEM_MGR) || (==PORTABLE_FIXED_SIZE_MEM_BLOCK_SCHEME) */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/portable/src/pmemory_ext.h b/portable/src/pmemory_ext.h
new file mode 100644
index 0000000..15db654
--- /dev/null
+++ b/portable/src/pmemory_ext.h
@@ -0,0 +1,47 @@
+/*---------------------------------------------------------------------------*
+ * pmemory_ext.h *
+ * *
+ * Copyright 2007, 2008 Nuance Communciations, Inc. *
+ * *
+ * 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. *
+ * *
+ *---------------------------------------------------------------------------*/
+
+
+
+#ifndef _PORTMEMORY_EXT_H
+#define _PORTMEMORY_EXT_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ /* TODO - removed PortNewAndTrace and PortDeleteAndTrace * Ian 06-March-2002 */
+
+ int PortMemoryInit(void);
+ void PortMemoryTerm(void);
+ void* PortNew(size_t sizeInBytes);
+ void PortDelete(void* objectPtr);
+ void PortMemTrackDump(void);
+ int PortGetMaxMemUsed(void);
+ int PortMemGetPoolSize(void);
+ void PortMemSetPoolSize(size_t sizeInBytes);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PORTMEMORY_H */
diff --git a/portable/src/pstream.c b/portable/src/pstream.c
new file mode 100644
index 0000000..e75d403
--- /dev/null
+++ b/portable/src/pstream.c
@@ -0,0 +1,990 @@
+/*---------------------------------------------------------------------------*
+ * pstream.c *
+ * *
+ * Copyright 2007, 2008 Nuance Communciations, Inc. *
+ * *
+ * 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 <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <string.h>
+#include "passert.h"
+#include "pstdio.h"
+#include "pmemory.h"
+#include "plog.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#ifdef PFILE_VIRTUAL_SUPPORT
+
+#define FILETEXTMODE 0x00
+#define FILEBINARYMODE 0x01
+#define FILEREADMODE 0x00
+#define FILEWRITEMODE 0x02
+
+ /* Open a existed writable file (i.e., the file is not closed yet).
+ At some cases user knows the filename only but does not know the file handle (1),
+ the user could call fopen to open this file again with another file handle (2).
+ He/She could get all the information before the last fflush was called via file handle (1).
+ */
+#define FILEREOPENMODE 0x08
+
+#define ISWRITEMODE(mode) (((mode)&FILEWRITEMODE) == FILEWRITEMODE)
+
+#define ISBINARYMODE(mode) (((mode)&FILEBINARYMODE) == FILEBINARYMODE)
+#define ISREOPENMODE(mode) (((mode)&FILEREOPENMODE) == FILEREOPENMODE)
+
+ /*
+ use a double link list to store the data of the writable file.
+ Each entry has 4k space (FILEBUFFERSIZE).
+ */
+#define FILEBUFFERSIZE 4096 /* 4k for each file buffer entry */
+
+ typedef struct FileBufferFrame
+ {
+ unsigned char *buffer; /* do not use pointer here and set it the first */
+ size_t size;
+ size_t index; /* nth buffer, from 0, 1, ... */
+ struct FileBufferFrame *prev;
+ struct FileBufferFrame *next;
+ BOOL bMalloc; /* flag, if the buffer malloced here ? */
+ }
+ FileBufferFrame;
+
+ FileRecord pWritableFileRecTable[] =
+ {
+ {"", 0, 0, 0, 3},
+ {"", 0, 0, 0, 3},
+ {"", 0, 0, 0, 3},
+ {"", 0, 0, 0, 3},
+ {"", 0, 0, 0, 3},
+ {"", 0, 0, 0, 3},
+ {"", 0, 0, 0, 3}
+ };
+ const nWritableFiles = sizeof(pWritableFileRecTable) / sizeof(pWritableFileRecTable[0]);
+
+#ifdef WIN32
+ extern const FileRecord pFileRecTable[];
+ extern const unsigned char pFileStart0[];
+#endif
+
+ const FileRecord *pReadOnlyFileRecTable = NULL;
+ const unsigned char *g_pFirstFile = NULL;
+
+ void SetFileTable(VirtualFileTable *table)
+ {
+#ifdef WIN32
+ pReadOnlyFileRecTable = pFileRecTable;
+ g_pFirstFile = pFileStart0;
+#else
+ if (table)
+ {
+ pReadOnlyFileRecTable = table->pFileTable;
+ g_pFirstFile = table->pFirstFile;
+ }
+#endif
+ }
+
+ /*
+ size: size of buffer.
+ buffer: is NULL, allocate here and set bMalloc as TRUE; otherwise use the external buffer
+ */
+ FileBufferFrame* CreateFileBufferFrame(size_t size, unsigned char *buffer)
+ {
+ FileBufferFrame *fb = NULL;
+
+ /* create FileBufferFrame */
+ fb = (FileBufferFrame *)MALLOC(sizeof(FileBufferFrame), "FileBufferFrame");
+ if (fb)
+ {
+ fb->next = NULL;
+ fb->prev = NULL;
+ fb->index = 0;
+ fb->size = size;
+ fb->bMalloc = FALSE;
+
+ if (buffer)
+ fb->buffer = buffer;
+ else
+ {
+ /* create one buffer frame */
+ if ((fb->buffer = (unsigned char *)MALLOC(size, "FileBufferFrame Buffer")) == NULL)
+ {
+ FREE(fb);
+ return NULL;
+ }
+ fb->bMalloc = TRUE;
+ }
+ }
+ return fb;
+ }
+
+ /* free FileBufferFrames
+ header should be the header of the FileBufferFrame link list
+ */
+ void DeleteFileBuffers(FileBufferFrame *header)
+ {
+ FileBufferFrame *next, *curr;
+
+ passert(header && header->prev == NULL); /* delete from the beginning */
+
+ curr = header;
+ do
+ {
+ next = curr->next;
+ if (curr->bMalloc)
+ FREE(curr->buffer);
+ FREE(curr);
+ curr = next;
+ }
+ while (next != NULL);
+ }
+
+ void PortFileInit(void)
+ {
+ /* No gPortStdin, gPortStdout, and gPortStderr to initialize. */
+#ifdef WIN32
+ pReadOnlyFileRecTable = pFileRecTable;
+ g_pFirstFile = pFileStart0;
+#endif
+ }
+
+ /* Assume that all files have at least one byte in them, that is length is > 0. */
+ PORT_FILE PortFopen(const char *filename, const char *mode)
+ {
+ char *pfn;
+ const unsigned char *start;
+ int size;
+ int text_mode;
+ int access_mode;
+ int m = 0;
+ PORT_FILE PortFile;
+ FileBufferFrame *curFrame;
+ size_t end;
+
+ passert(filename);
+ passert(mode);
+
+ if (pReadOnlyFileRecTable == NULL)
+ {
+ passert("File Table is not initialized!" == NULL);
+ return NULL;
+ }
+
+ /* support read and write. */
+ if (mode[0] == 'r' || mode[0] == 'w' || mode[0] == 'R') /* w means w+, attaching text */
+ {
+ char fname[80];
+ FileRecord *pCurRec;
+
+ access_mode = (mode[0] == 'r') ? FILEREADMODE : FILEWRITEMODE;
+
+ /* access mode: b/t */
+ if (mode[1] == '+')
+ text_mode = (mode[2] == 'b') ? FILEBINARYMODE : FILETEXTMODE;
+ else
+ text_mode = (mode[1] == 'b') ? FILEBINARYMODE : FILETEXTMODE;
+
+ /* Remove the directory path from the filename. */
+ if ((pfn = strrchr(filename, '/')) != NULL || (pfn = strrchr(filename, '\\')) != NULL)
+ strcpy(fname, pfn + 1);
+ else
+ strcpy(fname, filename);
+
+
+ /* Locate the start of the file, by looking through the file record table. */
+ if (access_mode == FILEREADMODE)
+ {
+ pCurRec = (FileRecord *)pReadOnlyFileRecTable;
+ start = g_pFirstFile;
+ }
+ else
+ {
+ pCurRec = (FileRecord *)pWritableFileRecTable;
+ }
+
+ while (pCurRec->size > 0 && strcmp(pCurRec->name, fname) != 0)
+ {
+ /* have to count the read-only file address in order to be best portable */
+ start += pCurRec->size;
+ pCurRec++;
+#ifndef NDEBUG
+ /* just for our internal test for read-only files.
+ if (pCurRec->start != NULL)
+ passert(start == pCurRec->start);
+ */
+#endif
+ }
+
+ m = access_mode | text_mode;
+ /* Do not support reopen the writable file now. */
+ if (access_mode == FILEREADMODE)
+ {
+ if (pCurRec->size == 0)
+ {
+ return NULL;
+ }
+
+ /* Found the file, record it's starting offset and length. */
+ end = pCurRec->end;
+ size = pCurRec->size;
+ }
+ /* writable file, open it the first time; could be text or binary */
+ else if (ISWRITEMODE(access_mode))
+ {
+ /* set the name and mode */
+ strcpy(pCurRec->name, fname);
+ pCurRec->mode = m;
+
+ start = pCurRec->start;
+ passert(start == NULL);
+ end = size = FILEBUFFERSIZE;
+ }
+ else
+ {
+ /* File doesn't exist. */
+ return NULL;
+ }
+ pfn = pCurRec->name;
+ }
+ else
+ {
+ /* Unknown mode */
+ return NULL;
+ }
+
+ /* Open file */
+ /* Create new file handle */
+ PortFile = (PORT_FILE)MALLOC(sizeof(PORT_FILE_HANDLE), "PortFile");
+ if (PortFile == NULL)
+ {
+ return NULL;
+ }
+
+ /* this mode is not tested yet */
+ if (ISREOPENMODE(m))
+ {
+ PortFile->startFrame = (FileBufferFrame *)start;
+ }
+ else
+ {
+ PortFile->startFrame = CreateFileBufferFrame(size, (unsigned char *)start);
+ if (ISWRITEMODE(m))
+ {
+ start = (const unsigned char *)PortFile->startFrame;
+ }
+ }
+
+ if (PortFile->startFrame == NULL)
+ {
+ FREE(PortFile);
+ return NULL;
+ }
+
+ PortFile->endFrame = PortFile->curFrame = PortFile->startFrame;
+
+ /* Mark that this file handle is for flash memory */
+ PortFile->filename = pfn;
+ PortFile->curPos = PortFile->curFrame->buffer;
+ PortFile->endPos = PortFile->curPos + end;
+
+ /* set the PortFile->endPos */
+ curFrame = PortFile->curFrame;
+ while (end > 0)
+ {
+ if (end > curFrame->size)
+ {
+ curFrame = curFrame->next;
+ passert(end > curFrame->size);
+ end -= curFrame->size;
+ passert(curFrame);
+ }
+ else
+ {
+ /* only reopen the writable file comes here */
+ PortFile->endPos = curFrame->buffer + end;
+ break;
+ }
+ }
+
+ PortFile->eof = 0; /* File must have at least one byte in it. */
+ PortFile->size = size;
+ PortFile->mode = m;
+
+ return PortFile;
+ }
+
+ int PortFclose(PORT_FILE PortFile)
+ {
+ passert(PortFile);
+
+ /* for reopen mode, do not delete the FileBufferFrame. Delete it by who created it */
+ if (ISWRITEMODE(PortFile->mode) && !ISREOPENMODE(PortFile->mode)) /* writable file */
+ {
+ int i = 0;
+ FileRecord *pCurRec = (FileRecord *)pWritableFileRecTable;
+
+ /* find the file record in memory */
+ for (i = 0; i < nWritableFiles; i++)
+ {
+ if (PortFile->size > 0 &&
+ PortFile->filename[0] != '\0' &&
+ strcmp(pCurRec->name, PortFile->filename) == 0
+ )
+ {
+ /* The parameter SREC.Recognizer.osi_log_level in par file control the output
+ # BIT 0 -> BASIC logging
+ # BIT 1 -> AUDIO waveform logging
+ # BIT 2 -> ADD WORD logging
+ # e.g. value is 3 = BASIC+AUDIO logging, no ADDWORD
+ SREC.Recognizer.osi_log_level = 7;
+
+ Do not control here
+ */
+ /*
+ SaveFileToDisk(PortFile);
+ */
+
+ pCurRec->name[0] = '\0';
+ pCurRec->start = NULL;
+ pCurRec->end = 0;
+ pCurRec->size = 0;
+
+ break;
+ }
+ pCurRec++;
+ }
+ }
+
+ DeleteFileBuffers(PortFile->startFrame);
+ FREE(PortFile);
+ return 0;
+ }
+
+ /*
+ * Returns the number of items read
+ */
+ size_t PortFread(void *buffer, size_t size, size_t count, PORT_FILE PortFile)
+ {
+ unsigned char *bufferPtr = (unsigned char *)buffer;
+ int cbRemain = size * count;
+ int cbAvail, minSize;
+ FileBufferFrame *curFrame = PortFile->curFrame;
+
+ passert(buffer);
+ passert(PortFile);
+
+ if (PortFile->eof == 1)
+ {
+ return 0;
+ }
+
+ while (cbRemain > 0)
+ {
+ if (PortFile->endPos == PortFile->curPos) /* end of file */
+ break;
+
+ if (PortFile->curPos == curFrame->buffer + curFrame->size) /* end of this frame */
+ {
+ /* go to next frame */
+ curFrame = PortFile->curFrame = curFrame->next;
+ PortFile->curPos = curFrame->buffer;
+ }
+
+ if (curFrame == PortFile->endFrame) /* last frame */
+ cbAvail = PortFile->endPos - PortFile->curPos;
+ else
+ cbAvail = curFrame->size - (PortFile->curPos - curFrame->buffer);
+
+ minSize = cbRemain < cbAvail ? cbRemain : cbAvail;
+ passert(minSize >= 0);
+
+ cbRemain -= minSize;
+ while (minSize-- > 0)
+ *bufferPtr++ = *PortFile->curPos++;
+ }
+
+ if (PortFile->curPos == PortFile->endPos)
+ {
+ PortFile->eof = 1;
+ }
+ /*
+ #ifdef __BIG_ENDIAN
+ if (!bNativeEnding)
+ {
+ swap_byte_order((char *)buffer, count, size);
+ }
+ #endif
+ */
+ return count - cbRemain / size;
+ }
+
+ /*
+ * Returns the number of items written
+ */
+ size_t PortFwrite(const void *data, size_t size, size_t count, PORT_FILE PortFile)
+ {
+ int cbWrite = size * count;
+ int cbAvail, minSize;
+ unsigned char *buffer = (unsigned char *)data;
+ FileBufferFrame *curFrame;
+
+ if (PortFile == NULL)
+ return 0;
+
+ curFrame = PortFile->curFrame;
+
+ /* write data until the end of the internal buffer */
+ if (PortFile->eof == 1)
+ {
+ /* TODO: should return 0, but it will cause infinite loop */
+ return 0;
+ }
+
+ /* why sub 1 ? */
+ while (cbWrite > 0)
+ {
+ if (PortFile->curPos == curFrame->buffer + curFrame->size) /* end of this frame */
+ {
+ if (curFrame->next == NULL)
+ {
+ /* assign a new space */
+ FileBufferFrame *nextFrame = CreateFileBufferFrame(FILEBUFFERSIZE, NULL);
+ if (nextFrame)
+ {
+ curFrame->next = nextFrame;
+ nextFrame->prev = curFrame;
+ nextFrame->index = curFrame->index + 1;
+
+ curFrame = PortFile->curFrame = nextFrame;
+ PortFile->endFrame = nextFrame;
+ PortFile->curPos = PortFile->endPos = nextFrame->buffer;
+
+ PortFile->size += FILEBUFFERSIZE;
+ }
+ else
+ {
+ return count -cbWrite / size;
+ }
+ }
+ else
+ curFrame = curFrame->next;
+ }
+
+ /* available space in current frame */
+ cbAvail = curFrame->size - (PortFile->curPos - curFrame->buffer);
+ minSize = cbWrite < cbAvail ? cbWrite : cbAvail;
+
+ memcpy((char *)PortFile->curPos, buffer, minSize);
+ buffer += minSize;
+ PortFile->curPos += minSize;
+ /* in case the write is not adding to the end */
+ if (curFrame == PortFile->endFrame && PortFile->endPos < PortFile->curPos)
+ PortFile->endPos = PortFile->curPos;
+ cbWrite -= minSize;
+ }
+
+ return count;
+ }
+
+ /*
+ * Returns 0 on success, non-zero on failure
+ */
+ int PortFseek(PORT_FILE PortFile, long offset, int origin)
+ {
+ int retval = 0;
+ int cbAvail, minSize;
+ FileBufferFrame *curFrame;
+
+ passert(PortFile);
+
+ /* Clear eof flag */
+ PortFile->eof = 0;
+
+ switch (origin)
+ {
+ case SEEK_CUR:
+ break;
+ case SEEK_END:
+ PortFile->curFrame = PortFile->endFrame;
+ PortFile->curPos = PortFile->endPos;
+ break;
+ case SEEK_SET:
+ PortFile->curFrame = PortFile->startFrame;
+ PortFile->curPos = PortFile->startFrame->buffer;
+ break;
+ default:
+ retval = 0; /* Error, unknown origin type */
+ break;
+ }
+
+ curFrame = PortFile->curFrame;
+
+ while (offset != 0)
+ {
+ if (offset > 0)
+ {
+ if (PortFile->endPos <= PortFile->curPos) /* end of file */
+ break;
+
+ if (PortFile->curPos == curFrame->buffer + curFrame->size) /* end of this frame */
+ {
+ /* go to next frame */
+ curFrame = curFrame->next;
+ if (curFrame == NULL)
+ break;
+ PortFile->curFrame = curFrame->next;
+ PortFile->curPos = curFrame->buffer;
+ }
+ if (curFrame == PortFile->endFrame) /* last frame */
+ cbAvail = PortFile->endPos - PortFile->curPos;
+ else
+ cbAvail = curFrame->size - (PortFile->curPos - curFrame->buffer);
+
+ minSize = offset < cbAvail ? offset : cbAvail;
+
+ PortFile->curPos += minSize;
+ offset -= minSize;
+ }
+ else
+ {
+ if (PortFile->startFrame->buffer == PortFile->curPos) /* start of file */
+ break;
+
+ if (PortFile->curPos <= curFrame->buffer) /* start of this frame */
+ {
+ /* go to next frame */
+ curFrame = curFrame->next;
+ if (curFrame == NULL)
+ break;
+ PortFile->curFrame = curFrame;
+ PortFile->curPos = curFrame->buffer + curFrame->size;
+ }
+ cbAvail = PortFile->curPos - curFrame->buffer;
+
+ minSize = -offset < cbAvail ? -offset : cbAvail;
+
+ PortFile->curPos -= minSize;
+ offset += minSize;
+ }
+ }
+ return retval;
+ }
+
+ /*
+ * Returns current file position
+ */
+ long PortFtell(PORT_FILE PortFile)
+ {
+ int size;
+ FileBufferFrame *curFrame = PortFile->curFrame;
+
+ passert(PortFile);
+
+ /* current Frame size */
+ size = PortFile->curPos - curFrame->buffer;
+
+ /* previous frame size */
+ while (curFrame = curFrame->prev)
+ size += curFrame->size;
+
+ return size;
+ }
+
+ int PortVfprintf(PORT_FILE PortFile, const char *format, va_list argptr)
+ {
+ char message[2*2048] = "";
+
+ /* Print formatted message to buffer */
+ vsprintf(message, format, argptr);
+
+ if (PortFile == NULL)
+ {
+ /* TODO: HECK to screen */
+#ifndef NDEBUG
+ printf(message);
+#endif
+ return 0;
+ }
+
+ passert(strlen(message) < 2*2048);
+ /* TO DO, seems at case fprintf(pf, "whatever"), message is empty! */
+ if (strlen(message) == 0)
+ return 0;
+ else
+ return PortFwrite(message, sizeof(char), strlen(message), PortFile);
+ }
+
+ /*
+ * Returns current file position
+ */
+ int PortFprintf(PORT_FILE PortFile, const char* format, ...)
+ {
+ va_list log_va_list;
+ int ret = 0;
+
+ /* Start variable argument list */
+ va_start(log_va_list, format);
+
+ /* Print formatted message to buffer */
+ ret = PortVfprintf(PortFile, format, log_va_list);
+
+ /* End variable argument list */
+ va_end(log_va_list);
+
+ return ret;
+ }
+
+ /*
+ * Returns string or NULL if error
+ */
+ char *PortFgets(char *string, int n, PORT_FILE PortFile)
+ {
+ int cbToRead = n - 1;
+ BOOL done = FALSE;
+ char *retString = NULL;
+ int i;
+
+ passert(string);
+ passert(n);
+ passert(PortFile);
+
+
+ if (PortFile->eof == 1)
+ {
+ return NULL;
+ }
+
+
+ /* Search for \n only! */
+ for (i = 0; i < cbToRead && !done; i++)
+ {
+ if (PortFile->curPos >= PortFile->endPos)
+ {
+ PortFile->eof = 1;
+ done = TRUE;
+ break;
+ }
+ else if (*PortFile->curPos == '\n')
+ {
+ if (retString == NULL)
+ {
+ retString = string;
+ }
+ retString[i] = '\n';
+ PortFile->curPos++;
+ done = TRUE;
+ }
+ else
+ {
+ if (retString == NULL)
+ {
+ retString = string;
+ }
+ retString[i] = *PortFile->curPos++;
+ }
+ }
+ if (retString != NULL)
+ {
+ retString[i] = '\0';
+ }
+ return retString;
+ }
+
+ /*
+ * Returns string or NULL if error
+ */
+ int PortFflush(PORT_FILE PortFile)
+ {
+ if (PortFile == NULL)
+ {
+ return -1;
+ }
+
+
+ /* call fflush before reopen a writable file */
+ if (ISWRITEMODE(PortFile->mode)) /* writable file */
+ {
+ FileRecord *pCurRec = (FileRecord *)pWritableFileRecTable;
+
+ /* find the file record in memory */
+ do
+ {
+ if (strcmp(pCurRec->name, PortFile->filename) == 0)
+ {
+ /* assgin it as startFrame, so others could get information when reopen it */
+ pCurRec->start = (unsigned char *)PortFile->startFrame;
+ pCurRec->end = PortFile->size - PortFile->endFrame->size +
+ (PortFile->endPos - PortFile->endFrame->buffer);
+ pCurRec->size = PortFile->size;
+ pCurRec->mode = PortFile->mode;
+
+ break;
+ }
+ pCurRec++;
+ }
+ while (pCurRec->size > 0);
+ }
+ return 0;
+ }
+
+
+ int PortFeof(PORT_FILE PortFile)
+ {
+ passert(PortFile);
+
+ return PortFile->eof;
+ }
+
+ /*
+ * Returns character or EOF
+ */
+ int PortFgetc(PORT_FILE PortFile)
+ {
+ int c;
+
+ passert(PortFile);
+
+ if (PortFile->eof == 1)
+ {
+ return EOF;
+ }
+ else
+ {
+ c = (int) * PortFile->curPos++;
+
+ if (PortFile->curPos >= PortFile->endPos)
+ {
+ PortFile->eof = 1;
+ }
+ }
+ return c;
+ }
+
+ /*
+ * Returns 0 if no error
+ */
+ int PortFerror(PORT_FILE PortFile)
+ {
+ passert(PortFile);
+
+ return 0;
+ }
+
+ void PortClearerr(PORT_FILE PortFile)
+ {
+ PortFile->eof = 0;
+ }
+
+ /*
+ * Returns current file position
+ */
+ int PortFscanf(PORT_FILE PortFile, const char* format, ...)
+ {
+ passert(PortFile);
+
+ (void)format;
+
+ /* Not supported. */
+ passert(FALSE);
+ return 0;
+ }
+
+ void PortRewind(PORT_FILE PortFile)
+ {
+ passert(PortFile);
+
+ PortFile->curFrame = PortFile->startFrame;
+ PortFile->curPos = PortFile->startFrame->buffer;
+
+ PortFile->eof = 0;
+ }
+
+ /*
+ * NULL if it fails, otherwise a valid file pointer
+ */
+ PORT_FILE PortFreopen(const char *path, const char *mode, PORT_FILE PortFile)
+ {
+ /* does not support reopen writable file */
+ if (PortFclose(PortFile) == 0)
+ {
+ PortFile = PortFopen(path, mode);
+ return PortFile;
+ }
+ return NULL;
+ }
+
+ char* PortGetcwd(char *buffer, int maxlen)
+ {
+ if (maxlen >= 1)
+ buffer[0] = '\0';
+ else
+ return NULL;
+
+ return buffer;
+ }
+
+ int PortMkdir(const char *dirname)
+ {
+ return 0;
+ }
+
+#ifdef XANAVI_PROJECT
+
+ int PortSaveFileToDisk(PORT_FILE PortFile, const char *path, const char *fname)
+ {
+ /* ### start mod */
+
+ FILE *fp = NULL; /* real file handle */
+ char fullfname[256], data[256];
+ char mode[3];
+ const char *file;
+ int size;
+
+ if (fname == NULL)
+ file = PortFile->filename;
+ else
+ file = fname;
+
+ if (path == NULL)
+ {
+ PLogMessage("trying to save file %s...\n", file);
+ sprintf(fullfname, "%s", file);
+ }
+ else
+ {
+ PLogMessage("trying to save file %s to %s...\n", file, path);
+ sprintf(fullfname, "%s/%s", path, file);
+ }
+
+ if (ISBINARYMODE(PortFile->mode)) /* binary file, the wav file */
+ {
+ sprintf(mode, "wb");
+ }
+ else
+ {
+ sprintf(mode, "w");
+ }
+
+ if ((fp = fopen(fullfname, mode)) != NULL)
+ {
+ PortRewind(PortFile);
+
+ while ((size = PortFread(data, 1, 256, PortFile)) > 0)
+ {
+ fwrite(data, 1, size, fp);
+ }
+ fclose(fp);
+ }
+ else
+ {
+ PLogError(L("Error to fopen %s with mode %s\n\n"), fullfname, mode);
+ return -1;
+ }
+ return 0;
+ }
+
+ int PortLoadFileFromDisk(PORT_FILE PortFile, const char *filename, const char *mode)
+ {
+ FILE *fp;
+ int size;
+ char data[256];
+
+ passert(PortFile);
+
+ if (filename == NULL)
+ filename = PortFile->filename;
+
+ if (mode == NULL)
+ {
+ data[0] = 'r';
+ if (ISBINARYMODE(PortFile->mode))
+ data[1] = 'b';
+ else
+ data[1] = '\0';
+ data[2] = '\0';
+ mode = data;
+ }
+
+ fp = fopen(filename, mode);
+
+ if (fp == NULL) /* do not have the file, it is fine */
+ return 0;
+
+ while ((size = fread(data, 1, 256, fp)) > 0)
+ PortFwrite(data, 1, size, PortFile);
+
+ fclose(fp);
+ /* go to the beginning of the file */
+ PortFseek(PortFile, 0, SEEK_SET);
+
+ return 0;
+ }
+
+ int XanaviSaveFileToDisk(PORT_FILE PortFile)
+ {
+ const char *tail;
+ int lenny;
+
+ passert(PortFile);
+
+ /* UG has to be 8.3 format! */
+ lenny = strlen(PortFile->filename);
+ if (lenny > 10)
+ tail = PortFile->filename + (lenny - 11);
+ else
+ tail = PortFile->filename;
+ /* printf( "8.3 filename is %s.\n", tail ); */
+
+ /* the 8.3 format has truncated the path in PortFile->filename,
+ should get the direcotry from par file
+ cmdline.DataCaptureDirectory = /CFC
+ TODO: here use /CFC directly to save time
+ */
+ return PortSaveFileToDisk(PortFile, "/CFC", tail);
+ }
+
+ int XanaviLoadFileFromDisk(PORT_FILE PortFile)
+ {
+ char fname[256];
+ char mode[3];
+
+ passert(PortFile);
+
+ sprintf(fname, "/CFC/%s", PortFile->filename);
+
+ mode[0] = 'r';
+ if (ISBINARYMODE(PortFile->mode))
+ mode[1] = 'b';
+ else
+ mode[1] = '\0';
+
+ mode[2] = '\0';
+
+ return PortLoadFileFromDisk(PortFile, fname, mode);
+ }
+
+#endif
+#endif /* STATIC_FILE_SYSTME */
+
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/portable/src/ptimer.c b/portable/src/ptimer.c
new file mode 100644
index 0000000..8cb72b9
--- /dev/null
+++ b/portable/src/ptimer.c
@@ -0,0 +1,264 @@
+/*---------------------------------------------------------------------------*
+ * ptimer.c *
+ * *
+ * Copyright 2007, 2008 Nuance Communciations, Inc. *
+ * *
+ * 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 "pmemory.h"
+#include "ptimer.h"
+#include "pmutex.h"
+
+#ifdef _WIN32
+
+/*
+ Note that this implementation assumes that QueryPerformanceCounter is
+ available (requires NT 3.1 and above) and that 64 bit arithmetic is
+ available (requires VC)
+*/
+
+struct PTimer_t
+{
+ LARGE_INTEGER PerformanceFreq;
+ LARGE_INTEGER RefTime;
+ LARGE_INTEGER elapsed;
+};
+
+
+
+/**
+ * Creates a new timer object.
+ **/
+ESR_ReturnCode PTimerCreate(PTimer **timer)
+{
+ PTimer *tmp = NULL;
+
+ if (timer == NULL)
+ return ESR_INVALID_ARGUMENT;
+ tmp = NEW(PTimer, "PTimer");
+ if (tmp == NULL)
+ return ESR_OUT_OF_MEMORY;
+
+ if (QueryPerformanceFrequency(&tmp->PerformanceFreq) == 0)
+ {
+ FREE(tmp);
+ return ESR_NOT_SUPPORTED;
+ }
+ tmp->PerformanceFreq.QuadPart /= 1000;
+
+ tmp->RefTime.QuadPart = 0;
+ tmp->elapsed.QuadPart = 0;
+ *timer = tmp;
+ return ESR_SUCCESS;
+}
+
+ESR_ReturnCode PTimerDestroy(PTimer *timer)
+{
+ if (timer == NULL) return ESR_INVALID_ARGUMENT;
+ FREE(timer);
+ return ESR_SUCCESS;
+}
+
+/**
+ * Starts the timer. This sets the reference time from which all new elapsed
+ * time are computed. This does not reset the elapsed time to 0. This is
+ * useful to pause the timer.
+ **/
+ESR_ReturnCode PTimerStart(PTimer *timer)
+{
+ if (timer == NULL) return ESR_INVALID_ARGUMENT;
+ return (QueryPerformanceCounter(&timer->RefTime) ?
+ ESR_SUCCESS :
+ ESR_NOT_SUPPORTED);
+}
+
+/**
+ * Stops the timer.
+ **/
+ESR_ReturnCode PTimerStop(PTimer *timer)
+{
+ if (timer == NULL) return ESR_INVALID_ARGUMENT;
+ if (timer->RefTime.QuadPart != 0)
+ {
+ LARGE_INTEGER now;
+ if (!QueryPerformanceCounter(&now)) return ESR_NOT_SUPPORTED;
+ timer->elapsed.QuadPart += now.QuadPart - timer->RefTime.QuadPart;
+ timer->RefTime.QuadPart = 0;
+ }
+ return ESR_SUCCESS;
+}
+
+/**
+ * Returns the timer elapsed time. If the Timer is in the stopped state,
+ * successive calls to getElapsed() will always return the same value. If
+ * the Timer is in the started state, successive calls will return the
+ * elapsed time since the last time PTimerStart() was called.
+ */
+ESR_ReturnCode PTimerGetElapsed(PTimer *timer, asr_uint32_t* elapsed)
+{
+ if (timer == NULL || elapsed == NULL)
+ return ESR_INVALID_ARGUMENT;
+
+ if (timer->RefTime.QuadPart != 0)
+ {
+ LARGE_INTEGER now;
+ if (!QueryPerformanceCounter(&now)) return ESR_NOT_SUPPORTED;
+ *elapsed = (asr_uint32_t) ((timer->elapsed.QuadPart + (now.QuadPart - timer->RefTime.QuadPart))
+ / timer->PerformanceFreq.QuadPart);
+ }
+ else
+ *elapsed = (asr_uint32_t) (timer->elapsed.QuadPart / timer->PerformanceFreq.QuadPart);
+
+ return ESR_SUCCESS;
+}
+
+
+/**
+ * Resets the elapsed time to 0 and resets the reference time of the Timer.
+ * This effectively reset the timer in the same state it was right after creation.
+ **/
+ESR_ReturnCode PTimerReset(PTimer *timer)
+{
+ if (timer == NULL) return ESR_INVALID_ARGUMENT;
+ timer->RefTime.QuadPart = 0;
+ timer->elapsed.QuadPart = 0;
+ return ESR_SUCCESS;
+}
+
+#elif defined(POSIX)
+#include "ptrd.h"
+/*
+POSIX has a timer
+*/
+/* Clocks and timers: clock_settime, clock_gettime, clock_getres, timer_xxx and nanosleep */
+#ifndef _POSIX_TIMERS
+#ifndef __vxworks /* __vxworks does not define it! */
+#error "Timer is not defined!"
+#endif /* __vxworks */
+#endif /* _POSIX_TIMERS */
+
+#define TIMER_MAX_VAL 10000
+
+struct PTimer_t
+{
+ timer_t timer;
+ asr_uint32_t elapsed;
+};
+
+/**
+* Creates a new timer object.
+**/
+ESR_ReturnCode PTimerCreate(PTimer **timer)
+{
+ PTimer *tmp = NULL;
+
+ if (timer == NULL) return ESR_INVALID_ARGUMENT;
+ tmp = NEW(PTimer, "PTimer");
+ if (tmp == NULL) return ESR_OUT_OF_MEMORY;
+
+ *timer = tmp;
+ if (timer_create(CLOCK_REALTIME, NULL, &(tmp->timer)) < 0)
+ return ESR_NOT_SUPPORTED;
+
+ return ESR_SUCCESS;
+}
+
+ESR_ReturnCode PTimerDestroy(PTimer *timer)
+{
+ if (timer == NULL) return ESR_INVALID_ARGUMENT;
+ timer_delete(timer->timer);
+ FREE(timer);
+
+ return ESR_SUCCESS;
+}
+
+/**
+* Starts the timer. This sets the reference time from which all new elapsed
+* time are computed. This does not reset the elapsed time to 0. This is
+* useful to pause the timer.
+**/
+ESR_ReturnCode PTimerStart(PTimer *timer)
+{
+ struct itimerspec expire_time;
+
+ if (timer == NULL) return ESR_INVALID_ARGUMENT;
+
+ expire_time.it_value.tv_sec = TIMER_MAX_VAL; /* set a large time for the timer */
+ expire_time.it_value.tv_nsec = 0;
+ return (timer_settime(timer->timer, 0, &expire_time, NULL) == 0 ?
+ ESR_SUCCESS :
+ ESR_NOT_SUPPORTED);
+}
+
+/**
+* Stops the timer.
+**/
+ESR_ReturnCode PTimerStop(PTimer *timer)
+{
+ struct itimerspec remaining;
+
+ if (timer == NULL) return ESR_INVALID_ARGUMENT;
+
+ if (timer_gettime(timer->timer, &remaining) != 0) return ESR_NOT_SUPPORTED;
+#if defined(__vxworks)
+ timer_cancel(timer->timer);
+#endif
+ timer->elapsed = (asr_uint32_t) ((TIMER_MAX_VAL - remaining.it_value.tv_sec) * SECOND2MSECOND
+ - remaining.it_value.tv_nsec / MSECOND2NSECOND);
+
+ return ESR_SUCCESS;
+}
+
+/**
+* Returns the timer elapsed time. If the Timer is in the stopped state,
+* successive calls to getElapsed() will always return the same value. If
+* the Timer is in the started state, successive calls will return the
+* elapsed time since the last time PTimerStart() was called.
+*/
+ESR_ReturnCode PTimerGetElapsed(PTimer *timer, asr_uint32_t* elapsed)
+{
+ if (timer == NULL || elapsed == NULL)
+ return ESR_INVALID_ARGUMENT;
+
+ if (timer->elapsed == 0) /* stop is not called */
+ {
+ struct itimerspec remaining;
+ if (timer_gettime(timer->timer, &remaining) != 0) return ESR_NOT_SUPPORTED;
+ *elapsed = (asr_uint32_t) ((TIMER_MAX_VAL - remaining.it_value.tv_sec) * SECOND2MSECOND
+ - remaining.it_value.tv_nsec / MSECOND2NSECOND);
+ }
+ else
+ *elapsed = timer->elapsed;
+
+ return ESR_SUCCESS;
+}
+
+
+/**
+* Resets the elapsed time to 0 and resets the reference time of the Timer.
+* This effectively reset the timer in the same state it was right after creation.
+**/
+ESR_ReturnCode PTimerReset(PTimer *timer)
+{
+ if (timer == NULL) return ESR_INVALID_ARGUMENT;
+ timer->elapsed = 0;
+ return ESR_SUCCESS;
+}
+
+#else
+#error "Ptimer not implemented for this platform."
+#endif
diff --git a/portable/src/ptimestamp.c b/portable/src/ptimestamp.c
new file mode 100644
index 0000000..6211c31
--- /dev/null
+++ b/portable/src/ptimestamp.c
@@ -0,0 +1,56 @@
+/*---------------------------------------------------------------------------*
+ * ptimestamp.c *
+ * *
+ * Copyright 2007, 2008 Nuance Communciations, Inc. *
+ * *
+ * 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 "ptimestamp.h"
+#include "pmutex.h"
+
+#ifdef _WIN32
+#include "sys/timeb.h"
+#endif
+
+void PTimeStampSet(PTimeStamp *timestamp)
+{
+
+#ifdef DISABLE_TIMESTAMPS
+ timestamp->secs = 0;
+ timestamp->msecs = 0;
+#else
+
+#ifdef _WIN32
+ struct _timeb now;
+
+ _ftime(&now);
+ timestamp->secs = now.time;
+ timestamp->msecs = now.millitm;
+#elif defined(POSIX)
+ struct timespec now;
+ clock_gettime(CLOCK_REALTIME, &now);
+ timestamp->secs = now.tv_sec;
+ timestamp->msecs = now.tv_nsec / MSECOND2NSECOND;
+#else
+#error "PTimeStampSet not defined for this platform."
+#endif
+
+#endif
+}
+
+PINLINE int PTimeStampDiff(const PTimeStamp *a, const PTimeStamp *b)
+{
+ return (a->secs - b->secs) * 1000 + a->msecs - b->msecs;
+}
diff --git a/portable/src/ptypes.c b/portable/src/ptypes.c
new file mode 100644
index 0000000..095a625
--- /dev/null
+++ b/portable/src/ptypes.c
@@ -0,0 +1,38 @@
+/*---------------------------------------------------------------------------*
+ * ptypes.c *
+ * *
+ * Copyright 2007, 2008 Nuance Communciations, Inc. *
+ * *
+ * 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 "ptypes.h"
+
+PINLINE ESR_BOOL isNumber(const LCHAR* str)
+{
+ if (!str || !*str)
+ return ESR_FALSE;
+
+ /* ignore minus sign */
+ if (*str == L('-'))
+ ++str;
+
+ while (*str)
+ {
+ if (!isdigit(*str))
+ return ESR_FALSE;
+ ++str;
+ }
+ return ESR_TRUE;
+}