2309 lines
69 KiB
C
2309 lines
69 KiB
C
/*
|
|
* Copyright (C) 2008-2009 SVOX AG, Baslerstr. 30, 8048 Zuerich, Switzerland
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/**
|
|
* @file picoos.c
|
|
*
|
|
* Copyright (C) 2008-2009 SVOX AG, Baslerstr. 30, 8048 Zuerich, Switzerland
|
|
* All rights reserved.
|
|
*
|
|
* History:
|
|
* - 2009-04-20 -- initial version
|
|
*
|
|
*/
|
|
|
|
#include <stdarg.h>
|
|
#include "picodefs.h"
|
|
#include "picopal.h"
|
|
#include "picoos.h"
|
|
#include "picodbg.h"
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
#if 0
|
|
}
|
|
#endif
|
|
|
|
|
|
#define picoos_SVOXFileHeader (picoos_char *)" (C) SVOX AG "
|
|
|
|
/* **********************************************
|
|
* default error and warning messages
|
|
* **********************************************/
|
|
|
|
|
|
#define PICOOS_MSG_EXC_NUMBER_FORMAT (picoos_char *) "wrong number format"
|
|
#define PICOOS_MSG_EXC_MAX_NUM_EXCEED (picoos_char *) "number exceeded"
|
|
#define PICOOS_MSG_EXC_NAME_CONFLICT (picoos_char *) "name conflict"
|
|
#define PICOOS_MSG_EXC_NAME_UNDEFINED (picoos_char *) "name undefined"
|
|
#define PICOOS_MSG_EXC_NAME_ILLEGAL (picoos_char *) "illegal name"
|
|
|
|
/* buffer interaction */
|
|
#define PICOOS_MSG_EXC_BUF_OVERFLOW (picoos_char *) "buffer overflow"
|
|
#define PICOOS_MSG_EXC_BUF_UNDERFLOW (picoos_char *) "buffer underflow"
|
|
#define PICOOS_MSG_EXC_BUF_IGNORE (picoos_char *) "buffer error"
|
|
|
|
/* memory allocation */
|
|
#define PICOOS_MSG_EXC_OUT_OF_MEM (picoos_char *) "out of memory"
|
|
|
|
/* files */
|
|
#define PICOOS_MSG_EXC_CANT_OPEN_FILE (picoos_char *) "cannot open file"
|
|
#define PICOOS_MSG_EXC_UNEXPECTED_FILE_TYPE (picoos_char *) "unexpected file type"
|
|
#define PICOOS_MSG_EXC_FILE_CORRUPT (picoos_char *) "corrupt file"
|
|
#define PICOOS_MSG_EXC_FILE_NOT_FOUND (picoos_char *) "file not found"
|
|
|
|
/* resources */
|
|
#define PICOOS_MSG_EXC_RESOURCE_BUSY (picoos_char *) "resource is busy"
|
|
#define PICOOS_MSG_EXC_RESOURCE_MISSING (picoos_char *) "cannot find resource"
|
|
|
|
/* knowledge bases */
|
|
#define PICOOS_MSG_EXC_KB_MISSING (picoos_char *) "knowledge base missing"
|
|
|
|
/* runtime exceptions (programming problems, usually a bug. E.g. trying to access null pointer) */
|
|
#define PICOOS_MSG_ERR_NULLPTR_ACCESS (picoos_char *) "access violation"
|
|
#define PICOOS_MSG_ERR_INVALID_HANDLE (picoos_char *) "invalid handle value"
|
|
#define PICOOS_MSG_ERR_INVALID_ARGUMENT (picoos_char *) "invalid argument supplied"
|
|
#define PICOOS_MSG_ERR_INDEX_OUT_OF_RANGE (picoos_char *) "index out of range"
|
|
|
|
|
|
/* errors ("external" errors, e.g. hardware failure. Usually cannot be dealt with from inside pico.) */
|
|
#define PICOOS_MSG_ERR_OTHER (picoos_char *) "other error"
|
|
|
|
#define PICOOS_MSG_ERR_PU (picoos_char *) "error in processing unit"
|
|
|
|
/* WARNINGS */
|
|
|
|
/* general */
|
|
#define PICOOS_MSG_WARN_INCOMPLETE (picoos_char *) "incomplete output"
|
|
#define PICOOS_MSG_WARN_FALLBACK (picoos_char *) "using fall-back"
|
|
#define PICOOS_MSG_WARN_OTHER (picoos_char *) "other warning"
|
|
|
|
/* resources */
|
|
#define PICOOS_MSG_WARN_KB_OVERWRITE (picoos_char *) "overwriting knowledge base"
|
|
#define PICOOS_MSG_WARN_RESOURCE_DOUBLE_LOAD (picoos_char *) "resource already loaded"
|
|
|
|
/* decision trees */
|
|
#define PICOOS_MSG_WARN_INVECTOR (picoos_char *) "input vector not constructed"
|
|
#define PICOOS_MSG_WARN_CLASSIFICATION (picoos_char *) "output not classified"
|
|
#define PICOOS_MSG_WARN_OUTVECTOR (picoos_char *) "output vector not decomposed"
|
|
|
|
/* processing units */
|
|
#define PICOOS_MSG_WARN_PU_IRREG_ITEM (picoos_char *) "irregular item in processing unit"
|
|
#define PICOOS_MSG_WARN_PU_DISCARD_BUF (picoos_char *) "discarding processing unit buffer"
|
|
|
|
|
|
/* **********************************************
|
|
* wrappers for picopal functions
|
|
* **********************************************/
|
|
|
|
picoos_int32 picoos_atoi(const picoos_char *s)
|
|
{
|
|
return (picoos_int32)picopal_atoi((const picoos_char *)s);
|
|
}
|
|
|
|
|
|
picoos_int8 picoos_strcmp(const picoos_char *a, const picoos_char *b)
|
|
{
|
|
picopal_int32 res = picopal_strcmp((const picopal_char *)a,
|
|
(const picopal_char *)b);
|
|
return (picoos_int8) ((res < 0) ? -1 : (res > 0) ? 1 : 0);
|
|
}
|
|
picoos_int8 picoos_strncmp(const picoos_char *a, const picoos_char *b, picoos_objsize_t siz)
|
|
{
|
|
picopal_int32 res = picopal_strncmp((const picopal_char *)a,
|
|
(const picopal_char *)b, siz);
|
|
return (picoos_int8) ((res < 0) ? -1 : (res > 0) ? 1 : 0);
|
|
}
|
|
|
|
picoos_uint32 picoos_strlen(const picoos_char *s)
|
|
{
|
|
return (picoos_uint32)picopal_strlen((const picopal_char *)s);
|
|
}
|
|
|
|
picoos_char *picoos_strchr(const picoos_char *s, picoos_char c)
|
|
{
|
|
return (picoos_char *)picopal_strchr((const picopal_char *)s,
|
|
(picopal_char)c);
|
|
}
|
|
|
|
picoos_char *picoos_strstr(const picoos_char *s, const picoos_char * substr)
|
|
{
|
|
return (picoos_char *)picopal_strstr((const picopal_char *)s,
|
|
(const picopal_char *)substr);
|
|
}
|
|
|
|
picoos_int16 picoos_slprintf(picoos_char * b, picoos_uint32 bsize, const picoos_char *f, ...)
|
|
{
|
|
picopal_int16 i;
|
|
va_list args;
|
|
|
|
va_start(args, (char *)f);
|
|
i = (picoos_int16)picopal_vslprintf((picoos_char *) b, bsize, (const picoos_char *)f, args);
|
|
va_end(args);
|
|
return i;
|
|
}
|
|
|
|
picoos_char *picoos_strcpy(picoos_char *d, const picoos_char *s)
|
|
{
|
|
return (picoos_char *)picopal_strcpy((picopal_char *)d,
|
|
(const picopal_char *)s);
|
|
}
|
|
|
|
picoos_char *picoos_strcat(picoos_char *d, const picoos_char *s)
|
|
{
|
|
return (picoos_char *)picopal_strcat((picopal_char *)d,
|
|
(const picopal_char *)s);
|
|
}
|
|
|
|
picoos_objsize_t picoos_strlcpy(picoos_char *dst, const picoos_char *src, picoos_objsize_t siz)
|
|
{
|
|
return (picoos_objsize_t) picopal_strlcpy((picopal_char *) dst, (const picopal_char *) src, (picopal_objsize_t) siz);
|
|
}
|
|
|
|
/* copies 'length' bytes from 'src' to 'dest'. (regions may be overlapping) no error checks! */
|
|
void * picoos_mem_copy(const void * src, void * dst, picoos_objsize_t length)
|
|
{
|
|
return picopal_mem_copy(src,dst,(picopal_objsize_t) length);
|
|
}
|
|
|
|
/* sets 'length' bytes starting at dest[0] to 'byte_val' */
|
|
void * picoos_mem_set(void * dest, picoos_uint8 byte_val, picoos_objsize_t length) {
|
|
return picopal_mem_set(dest,(picopal_uint8)byte_val, (picopal_objsize_t)length);
|
|
}
|
|
|
|
|
|
picoos_double picoos_cos (const picoos_double cos_arg)
|
|
{
|
|
return (picoos_double) picopal_cos ((picopal_double) cos_arg);
|
|
}
|
|
|
|
|
|
picoos_double picoos_sin (const picoos_double sin_arg)
|
|
{
|
|
return (picoos_double) picopal_sin((picopal_double) sin_arg);
|
|
}
|
|
picoos_double picoos_fabs (const picoos_double fabs_arg)
|
|
{
|
|
return (picoos_double) picopal_fabs((picopal_double) fabs_arg);
|
|
}
|
|
|
|
picoos_double picoos_quick_exp(const picoos_double y) {
|
|
return (picoos_double) picopal_quick_exp ((picopal_double)y);
|
|
}
|
|
|
|
|
|
/* *****************************************************************/
|
|
/* "Common" */
|
|
/* *****************************************************************/
|
|
/* picoos_common is a collection of basic functionalities that must be globally
|
|
* accessible from every "big" function. It includes pointers to the MemoryManasger,
|
|
* ExceptionManager and a system-wide list of open files. */
|
|
|
|
picoos_Common picoos_newCommon(picoos_MemoryManager mm)
|
|
{
|
|
picoos_Common this = (picoos_Common) picoos_allocate(mm,sizeof(*this));
|
|
if (NULL != this) {
|
|
/* initialize */
|
|
this->em = NULL;
|
|
this->mm = NULL;
|
|
this->fileList = NULL;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
void picoos_disposeCommon(picoos_MemoryManager mm, picoos_Common * this)
|
|
{
|
|
if (NULL != (*this)) {
|
|
/* terminate */
|
|
picoos_deallocate(mm,(void *)this);
|
|
}
|
|
}
|
|
|
|
|
|
/* *****************************************************************/
|
|
/* Memory Management */
|
|
/* *****************************************************************/
|
|
|
|
typedef struct mem_block_hdr * MemBlockHdr;
|
|
typedef struct mem_block_hdr
|
|
{
|
|
MemBlockHdr next;
|
|
byte_ptr_t data;
|
|
picoos_objsize_t size;
|
|
} mem_block_hdr_t;
|
|
|
|
typedef struct mem_cell_hdr * MemCellHdr;
|
|
typedef struct mem_cell_hdr
|
|
{
|
|
/* size may be <0 if used */
|
|
picoos_ptrdiff_t size;
|
|
MemCellHdr leftCell;
|
|
MemCellHdr prevFree, nextFree;
|
|
} mem_cell_hdr_t;
|
|
|
|
typedef struct memory_manager
|
|
{
|
|
MemBlockHdr firstBlock, lastBlock; /* memory blockList */
|
|
MemCellHdr freeCells, lastFree; /* free memory cells (first/last sentinel */
|
|
/* "constants" */
|
|
picoos_objsize_t fullCellHdrSize; /* aligned size of full cell header, including free-links */
|
|
picoos_objsize_t usedCellHdrSize; /* aligned size of header part without free-links */
|
|
picoos_objsize_t minContSize; /* minimum requestable application content size for allocation;
|
|
must hold free-list info; = fullCellHdrSize-usedCellHdrSize */
|
|
picoos_objsize_t minCellSize; /* minimum remaining cell size when a free cell is split */
|
|
picoos_bool protMem; /* true if memory protection is enabled */
|
|
picoos_ptrdiff_t usedSize;
|
|
picoos_ptrdiff_t prevUsedSize;
|
|
picoos_ptrdiff_t maxUsedSize;
|
|
} memory_manager_t;
|
|
|
|
/** allocates 'alloc_size' bytes at start of raw memory block ('raw_mem',raw_mem_size)
|
|
* and returns pointer to allocated region. Returns remaining (correctly aligned) raw memory block
|
|
* in ('rest_mem','rest_mem_size').
|
|
* The allocated memory is not subject to memory management, so that it can never be freed again!
|
|
*
|
|
*/
|
|
void * picoos_raw_malloc(byte_ptr_t raw_mem,
|
|
picoos_objsize_t raw_mem_size, picoos_objsize_t alloc_size,
|
|
byte_ptr_t * rest_mem, picoos_objsize_t * rest_mem_size)
|
|
{
|
|
picoos_ptrdiff_t rest;
|
|
if (raw_mem == NULL) {
|
|
return NULL;
|
|
} else {
|
|
if (alloc_size < 1) {
|
|
alloc_size = 1;
|
|
}
|
|
alloc_size = ((alloc_size + PICOOS_ALIGN_SIZE - 1) / PICOOS_ALIGN_SIZE)
|
|
* PICOOS_ALIGN_SIZE;
|
|
|
|
rest = raw_mem_size - alloc_size;
|
|
if (rest < 0) {
|
|
return NULL;
|
|
} else {
|
|
*rest_mem_size = rest;
|
|
*rest_mem = raw_mem + alloc_size;
|
|
return (void *) raw_mem;
|
|
}
|
|
}
|
|
}
|
|
|
|
/** initializes the last block of mm */
|
|
static int os_init_mem_block(picoos_MemoryManager this)
|
|
{
|
|
int isFirstBlock;
|
|
void * newBlockAddr;
|
|
picoos_objsize_t size;
|
|
MemCellHdr cbeg, cmid, cend;
|
|
|
|
isFirstBlock = (this->freeCells == NULL);
|
|
newBlockAddr = (void *) this->lastBlock->data;
|
|
size = this->lastBlock->size;
|
|
cbeg = (MemCellHdr) newBlockAddr;
|
|
cmid = (MemCellHdr)((picoos_objsize_t)newBlockAddr + this->fullCellHdrSize);
|
|
cend = (MemCellHdr)((picoos_objsize_t)newBlockAddr + size
|
|
- this->fullCellHdrSize);
|
|
cbeg->size = 0;
|
|
|
|
cbeg->leftCell = NULL;
|
|
cmid->size = size - 2 * this->fullCellHdrSize;
|
|
cmid->leftCell = cbeg;
|
|
cend->size = 0;
|
|
cend->leftCell = cmid;
|
|
if (isFirstBlock) {
|
|
cbeg->nextFree = cmid;
|
|
cbeg->prevFree = NULL;
|
|
cmid->nextFree = cend;
|
|
cmid->prevFree = cbeg;
|
|
cend->nextFree = NULL;
|
|
cend->prevFree = cmid;
|
|
this->freeCells = cbeg;
|
|
this->lastFree = cend;
|
|
} else {
|
|
/* add cmid to free cell list */
|
|
cbeg->nextFree = NULL;
|
|
cbeg->prevFree = NULL;
|
|
cmid->nextFree = this->freeCells->nextFree;
|
|
cmid->prevFree = this->freeCells;
|
|
cmid->nextFree->prevFree = cmid;
|
|
cmid->prevFree->nextFree = cmid;
|
|
cend->nextFree = NULL;
|
|
cbeg->prevFree = NULL;
|
|
}
|
|
return PICO_OK;
|
|
}
|
|
|
|
|
|
picoos_MemoryManager picoos_newMemoryManager(
|
|
void *raw_memory,
|
|
picoos_objsize_t size,
|
|
picoos_bool enableMemProt)
|
|
{
|
|
byte_ptr_t rest_mem;
|
|
picoos_objsize_t rest_mem_size;
|
|
picoos_MemoryManager this;
|
|
picoos_objsize_t size2;
|
|
mem_cell_hdr_t test_cell;
|
|
|
|
this = picoos_raw_malloc(raw_memory, size, sizeof(memory_manager_t),
|
|
&rest_mem, &rest_mem_size);
|
|
if (this == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
/* test if memory protection functionality is available on the current
|
|
platform (if not, picopal_mpr_alloc() always returns NULL) */
|
|
if (enableMemProt) {
|
|
void *addr = picopal_mpr_alloc(100);
|
|
if (addr == NULL) {
|
|
enableMemProt = FALSE;
|
|
} else {
|
|
picopal_mpr_free(&addr);
|
|
}
|
|
}
|
|
|
|
this->firstBlock = NULL;
|
|
this->lastBlock = NULL;
|
|
this->freeCells = NULL;
|
|
this->lastFree = NULL;
|
|
|
|
this->protMem = enableMemProt;
|
|
this->usedSize = 0;
|
|
this->prevUsedSize = 0;
|
|
this->maxUsedSize = 0;
|
|
|
|
/* get aligned full header size */
|
|
this->fullCellHdrSize = ((sizeof(mem_cell_hdr_t) + PICOOS_ALIGN_SIZE - 1)
|
|
/ PICOOS_ALIGN_SIZE) * PICOOS_ALIGN_SIZE;
|
|
/* get aligned size of header without free-list fields; the result may be compiler-dependent;
|
|
the size is therefore computed by inspecting the end addresses of the fields 'size' and 'leftCell';
|
|
the higher of the ending addresses is used to get the (aligned) starting address
|
|
of the application contents */
|
|
this->usedCellHdrSize = (picoos_objsize_t) &test_cell.size
|
|
- (picoos_objsize_t) &test_cell + sizeof(picoos_objsize_t);
|
|
size2 = (picoos_objsize_t) &test_cell.leftCell - (picoos_objsize_t)
|
|
&test_cell + sizeof(MemCellHdr);
|
|
if (size2 > this->usedCellHdrSize) {
|
|
this->usedCellHdrSize = size2;
|
|
}
|
|
/* get minimum application-usable size; must be large enough to hold remainder of
|
|
cell header (free-list links) when in free-list */
|
|
this->minContSize = this->fullCellHdrSize - this->usedCellHdrSize;
|
|
/* get minimum required size of a cell remaining after a cell split */
|
|
this->minCellSize = this->fullCellHdrSize + PICOOS_ALIGN_SIZE;
|
|
|
|
/* install remainder of raw memory block as first block */
|
|
raw_memory = rest_mem;
|
|
size = rest_mem_size;
|
|
this->firstBlock = this->lastBlock = picoos_raw_malloc(raw_memory, size,
|
|
sizeof(mem_block_hdr_t), &rest_mem, &rest_mem_size);
|
|
if (this->lastBlock == NULL) {
|
|
return NULL;
|
|
}
|
|
this->lastBlock->next = NULL;
|
|
this->lastBlock->data = rest_mem;
|
|
this->lastBlock->size = rest_mem_size;
|
|
|
|
os_init_mem_block(this);
|
|
|
|
return this;
|
|
}
|
|
|
|
void picoos_disposeMemoryManager(picoos_MemoryManager * mm)
|
|
{
|
|
*mm = NULL;
|
|
}
|
|
|
|
|
|
/* the following memory manager routines are for testing and
|
|
debugging purposes */
|
|
|
|
|
|
void *picoos_allocProtMem(picoos_MemoryManager mm, picoos_objsize_t byteSize)
|
|
{
|
|
if (mm->protMem) {
|
|
return picopal_mpr_alloc(byteSize);
|
|
} else {
|
|
return picoos_allocate(mm, byteSize);
|
|
}
|
|
}
|
|
|
|
|
|
void picoos_deallocProtMem(picoos_MemoryManager mm, void **addr)
|
|
{
|
|
if (mm->protMem) {
|
|
picopal_mpr_free(addr);
|
|
} else {
|
|
picoos_deallocate(mm, addr);
|
|
}
|
|
}
|
|
|
|
|
|
void picoos_protectMem(
|
|
picoos_MemoryManager mm,
|
|
void *addr,
|
|
picoos_objsize_t len,
|
|
picoos_bool enable)
|
|
{
|
|
if (mm->protMem) {
|
|
int prot = PICOPAL_PROT_READ;
|
|
if (!enable) {
|
|
prot |= PICOPAL_PROT_WRITE;
|
|
}
|
|
picopal_mpr_protect(addr, len, prot);
|
|
} else {
|
|
/* memory protection disabled; nothing to do */
|
|
}
|
|
}
|
|
|
|
#define PICOPAL_PROT_NONE 0 /* the memory cannot be accessed at all */
|
|
#define PICOPAL_PROT_READ 1 /* the memory can be read */
|
|
#define PICOPAL_PROT_WRITE 2 /* the memory can be written to */
|
|
|
|
void picoos_getMemUsage(
|
|
picoos_MemoryManager this,
|
|
picoos_bool resetIncremental,
|
|
picoos_int32 *usedBytes,
|
|
picoos_int32 *incrUsedBytes,
|
|
picoos_int32 *maxUsedBytes)
|
|
{
|
|
*usedBytes = (picoos_int32) this->usedSize;
|
|
*incrUsedBytes = (picoos_int32) (this->usedSize - this->prevUsedSize);
|
|
*maxUsedBytes = (picoos_int32) this->maxUsedSize;
|
|
if (resetIncremental) {
|
|
this->prevUsedSize = this->usedSize;
|
|
}
|
|
}
|
|
|
|
|
|
void picoos_showMemUsage(picoos_MemoryManager this, picoos_bool incremental,
|
|
picoos_bool resetIncremental)
|
|
{
|
|
picoos_int32 usedBytes, incrUsedBytes, maxUsedBytes;
|
|
|
|
picoos_getMemUsage(this, resetIncremental, &usedBytes, &incrUsedBytes,
|
|
&maxUsedBytes);
|
|
if (incremental) {
|
|
PICODBG_DEBUG(("additional memory used: %d", incrUsedBytes));
|
|
} else {
|
|
PICODBG_DEBUG(("memory used: %d, maximally used: %d", usedBytes, maxUsedBytes));
|
|
}
|
|
}
|
|
|
|
|
|
void * picoos_allocate(picoos_MemoryManager this,
|
|
picoos_objsize_t byteSize)
|
|
{
|
|
|
|
picoos_objsize_t cellSize;
|
|
MemCellHdr c, c2, c2r;
|
|
void * adr;
|
|
|
|
if (byteSize < this->minContSize) {
|
|
byteSize = this->minContSize;
|
|
}
|
|
byteSize = ((byteSize + PICOOS_ALIGN_SIZE - 1) / PICOOS_ALIGN_SIZE)
|
|
* PICOOS_ALIGN_SIZE;
|
|
|
|
cellSize = byteSize + this->usedCellHdrSize;
|
|
/*PICODBG_TRACE(("allocating %d", cellSize));*/
|
|
c = this->freeCells->nextFree;
|
|
while (
|
|
(c != NULL) &&
|
|
(c->size != (picoos_ptrdiff_t) cellSize) &&
|
|
(c->size < (picoos_ptrdiff_t)(cellSize+ this->minCellSize))) {
|
|
c = c->nextFree;
|
|
}
|
|
if (c == NULL) {
|
|
return NULL;
|
|
}
|
|
if ((c->size == (picoos_ptrdiff_t) cellSize)) {
|
|
c->prevFree->nextFree = c->nextFree;
|
|
c->nextFree->prevFree = c->prevFree;
|
|
} else {
|
|
c2 = (MemCellHdr)((picoos_objsize_t)c + cellSize);
|
|
c2->size = c->size - cellSize;
|
|
c->size = cellSize;
|
|
c2->leftCell = c;
|
|
c2r = (MemCellHdr)((picoos_objsize_t)c2 + c2->size);
|
|
c2r->leftCell = c2;
|
|
c2->nextFree = c->nextFree;
|
|
c2->nextFree->prevFree = c2;
|
|
c2->prevFree = c->prevFree;
|
|
c2->prevFree->nextFree = c2;
|
|
}
|
|
|
|
/* statistics */
|
|
this->usedSize += cellSize;
|
|
if (this->usedSize > this->maxUsedSize) {
|
|
this->maxUsedSize = this->usedSize;
|
|
}
|
|
|
|
c->size = -(c->size);
|
|
adr = (void *)((picoos_objsize_t)c + this->usedCellHdrSize);
|
|
return adr;
|
|
}
|
|
|
|
void picoos_deallocate(picoos_MemoryManager this, void * * adr)
|
|
{
|
|
MemCellHdr c;
|
|
MemCellHdr cr;
|
|
MemCellHdr cl;
|
|
MemCellHdr crr;
|
|
|
|
|
|
if ((*adr) != NULL) {
|
|
c = (MemCellHdr)((picoos_objsize_t)(*adr) - this->usedCellHdrSize);
|
|
c->size = -(c->size);
|
|
|
|
/*PICODBG_TRACE(("deallocating %d", c->size));*/
|
|
/* statistics */
|
|
this->usedSize -= c->size;
|
|
|
|
cr = (MemCellHdr)((picoos_objsize_t)c + c->size);
|
|
cl = c->leftCell;
|
|
if (cl->size > 0) {
|
|
if (cr->size > 0) {
|
|
crr = (MemCellHdr)((picoos_objsize_t)cr + cr->size);
|
|
crr->leftCell = cl;
|
|
cl->size = ((cl->size + c->size) + cr->size);
|
|
cr->nextFree->prevFree = cr->prevFree;
|
|
cr->prevFree->nextFree = cr->nextFree;
|
|
} else {
|
|
cl->size = (cl->size + c->size);
|
|
cr->leftCell = cl;
|
|
}
|
|
} else {
|
|
if ((cr->size > 0)) {
|
|
crr = (MemCellHdr)((picoos_objsize_t)cr + cr->size);
|
|
crr->leftCell = c;
|
|
c->size = (c->size + cr->size);
|
|
c->nextFree = cr->nextFree;
|
|
c->prevFree = cr->prevFree;
|
|
c->nextFree->prevFree = c;
|
|
c->prevFree->nextFree = c;
|
|
} else {
|
|
c->nextFree = this->freeCells->nextFree;
|
|
c->prevFree = this->freeCells;
|
|
c->nextFree->prevFree = c;
|
|
c->prevFree->nextFree = c;
|
|
}
|
|
}
|
|
}
|
|
*adr = NULL;
|
|
}
|
|
|
|
/* *****************************************************************/
|
|
/* Exception Management */
|
|
/* *****************************************************************/
|
|
/** object : exceptionManager
|
|
* shortcut : em
|
|
*
|
|
*/
|
|
|
|
typedef picoos_char picoos_exc_msg[PICOOS_MAX_EXC_MSG_LEN];
|
|
typedef picoos_char picoos_warn_msg[PICOOS_MAX_WARN_MSG_LEN];
|
|
|
|
typedef struct picoos_exception_manager
|
|
{
|
|
picoos_int32 curExceptionCode;
|
|
picoos_exc_msg curExceptionMessage;
|
|
|
|
picoos_uint8 curNumWarnings;
|
|
picoos_int32 curWarningCode[PICOOS_MAX_NUM_WARNINGS];
|
|
picoos_warn_msg curWarningMessage[PICOOS_MAX_NUM_WARNINGS];
|
|
|
|
} picoos_exception_manager_t;
|
|
|
|
void picoos_emReset(picoos_ExceptionManager this)
|
|
{
|
|
this->curExceptionCode = PICO_OK;
|
|
this->curExceptionMessage[0] = '\0';
|
|
this->curNumWarnings = 0;
|
|
}
|
|
|
|
picoos_ExceptionManager picoos_newExceptionManager(picoos_MemoryManager mm)
|
|
{
|
|
picoos_ExceptionManager this = (picoos_ExceptionManager) picoos_allocate(
|
|
mm, sizeof(*this));
|
|
if (NULL != this) {
|
|
/* initialize */
|
|
picoos_emReset(this);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
void picoos_disposeExceptionManager(picoos_MemoryManager mm,
|
|
picoos_ExceptionManager * this)
|
|
{
|
|
if (NULL != (*this)) {
|
|
/* terminate */
|
|
picoos_deallocate(mm, (void *)this);
|
|
}
|
|
}
|
|
|
|
static void picoos_vSetErrorMsg(picoos_char * dst, picoos_objsize_t siz,
|
|
picoos_int16 code, picoos_char * base, const picoos_char *fmt, va_list args)
|
|
{
|
|
picoos_uint16 bsize;
|
|
|
|
if (NULL == base) {
|
|
switch (code) {
|
|
case PICO_EXC_NUMBER_FORMAT:
|
|
base = PICOOS_MSG_EXC_NUMBER_FORMAT;
|
|
break;
|
|
case PICO_EXC_MAX_NUM_EXCEED:
|
|
base = PICOOS_MSG_EXC_MAX_NUM_EXCEED;
|
|
break;
|
|
case PICO_EXC_NAME_CONFLICT:
|
|
base = PICOOS_MSG_EXC_NAME_CONFLICT;
|
|
break;
|
|
case PICO_EXC_NAME_UNDEFINED:
|
|
base = PICOOS_MSG_EXC_NAME_UNDEFINED;
|
|
break;
|
|
case PICO_EXC_NAME_ILLEGAL:
|
|
base = PICOOS_MSG_EXC_NAME_ILLEGAL;
|
|
break;
|
|
|
|
/* buffer interaction */
|
|
case PICO_EXC_BUF_OVERFLOW:
|
|
base = PICOOS_MSG_EXC_BUF_OVERFLOW;
|
|
break;
|
|
case PICO_EXC_BUF_UNDERFLOW:
|
|
base = PICOOS_MSG_EXC_BUF_UNDERFLOW;
|
|
break;
|
|
case PICO_EXC_BUF_IGNORE:
|
|
base = PICOOS_MSG_EXC_BUF_IGNORE;
|
|
break;
|
|
|
|
/* memory allocation */
|
|
case PICO_EXC_OUT_OF_MEM:
|
|
base = PICOOS_MSG_EXC_OUT_OF_MEM;
|
|
break;
|
|
|
|
/* files */
|
|
case PICO_EXC_CANT_OPEN_FILE:
|
|
base = PICOOS_MSG_EXC_CANT_OPEN_FILE;
|
|
break;
|
|
case PICO_EXC_UNEXPECTED_FILE_TYPE:
|
|
base = PICOOS_MSG_EXC_UNEXPECTED_FILE_TYPE;
|
|
break;
|
|
case PICO_EXC_FILE_CORRUPT:
|
|
base = PICOOS_MSG_EXC_FILE_CORRUPT;
|
|
break;
|
|
|
|
case PICO_EXC_FILE_NOT_FOUND:
|
|
base = PICOOS_MSG_EXC_FILE_NOT_FOUND;
|
|
break;
|
|
|
|
/* resources */
|
|
case PICO_EXC_RESOURCE_BUSY:
|
|
base = PICOOS_MSG_EXC_RESOURCE_BUSY;
|
|
break;
|
|
case PICO_EXC_RESOURCE_MISSING:
|
|
base = PICOOS_MSG_EXC_RESOURCE_MISSING;
|
|
break;
|
|
|
|
/* knowledge bases */
|
|
case PICO_EXC_KB_MISSING:
|
|
fmt = PICOOS_MSG_EXC_KB_MISSING;
|
|
break;
|
|
|
|
/* runtime exceptions (programming problems, usually a bug. E.g. trying to access null pointer) */
|
|
case PICO_ERR_NULLPTR_ACCESS:
|
|
base = PICOOS_MSG_ERR_NULLPTR_ACCESS;
|
|
break;
|
|
case PICO_ERR_INVALID_HANDLE:
|
|
base = PICOOS_MSG_ERR_INVALID_HANDLE;
|
|
break;
|
|
case PICO_ERR_INVALID_ARGUMENT:
|
|
base = PICOOS_MSG_ERR_INVALID_ARGUMENT;
|
|
break;
|
|
case PICO_ERR_INDEX_OUT_OF_RANGE:
|
|
base = PICOOS_MSG_ERR_INDEX_OUT_OF_RANGE;
|
|
break;
|
|
|
|
/* errors ("external" errors, e.g. hardware failure. Usually cannot be dealt with from inside pico.) */
|
|
case PICO_ERR_OTHER:
|
|
base = PICOOS_MSG_ERR_OTHER;
|
|
break;
|
|
|
|
/* other error inside pu */
|
|
case PICO_STEP_ERROR:
|
|
base = PICOOS_MSG_ERR_PU;
|
|
break;
|
|
|
|
/* WARNINGS */
|
|
|
|
/* general */
|
|
case PICO_WARN_INCOMPLETE:
|
|
base = PICOOS_MSG_WARN_INCOMPLETE;
|
|
break;
|
|
case PICO_WARN_FALLBACK:
|
|
base = PICOOS_MSG_WARN_FALLBACK;
|
|
break;
|
|
|
|
case PICO_WARN_OTHER:
|
|
base = PICOOS_MSG_WARN_OTHER;
|
|
break;
|
|
|
|
/* resources */
|
|
case PICO_WARN_KB_OVERWRITE:
|
|
base = PICOOS_MSG_WARN_KB_OVERWRITE;
|
|
break;
|
|
case PICO_WARN_RESOURCE_DOUBLE_LOAD:
|
|
base = PICOOS_MSG_WARN_RESOURCE_DOUBLE_LOAD;
|
|
break;
|
|
|
|
/* decision trees */
|
|
case PICO_WARN_INVECTOR:
|
|
base = PICOOS_MSG_WARN_INVECTOR;
|
|
break;
|
|
case PICO_WARN_CLASSIFICATION:
|
|
base = PICOOS_MSG_WARN_CLASSIFICATION;
|
|
break;
|
|
case PICO_WARN_OUTVECTOR:
|
|
base = PICOOS_MSG_WARN_OUTVECTOR;
|
|
break;
|
|
|
|
/* processing units */
|
|
case PICO_WARN_PU_IRREG_ITEM:
|
|
base = PICOOS_MSG_WARN_PU_IRREG_ITEM;
|
|
break;
|
|
case PICO_WARN_PU_DISCARD_BUF:
|
|
base = PICOOS_MSG_WARN_PU_DISCARD_BUF;
|
|
break;
|
|
|
|
default:
|
|
base = (picoos_char *) "unknown error";
|
|
break;
|
|
}
|
|
}
|
|
bsize = picoos_strlcpy(dst,base,siz);
|
|
if ((NULL != fmt) && (bsize < siz)) { /* there is something to add and more space to add it */
|
|
if (bsize > 0) {
|
|
dst += bsize;
|
|
siz -= bsize;
|
|
bsize = picoos_strlcpy(dst,(picoos_char *)": ",siz);
|
|
}
|
|
if (bsize < siz) {
|
|
picopal_vslprintf((picopal_char *) dst + bsize, siz - bsize, (picopal_char *)fmt, args);
|
|
}
|
|
}
|
|
}
|
|
|
|
void picoos_setErrorMsg(picoos_char * dst, picoos_objsize_t siz,
|
|
picoos_int16 code, picoos_char * base, const picoos_char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, (char *)fmt);
|
|
picoos_vSetErrorMsg(dst,siz, code, base, fmt,args);
|
|
va_end(args);
|
|
}
|
|
|
|
/* For convenience, this function returns the resulting current exception code. The return value therefore is NOT the status of raising
|
|
* the error! */
|
|
pico_status_t picoos_emRaiseException(picoos_ExceptionManager this,
|
|
pico_status_t exceptionCode, picoos_char * baseMessage, picoos_char * fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
|
|
if (PICO_OK == this->curExceptionCode && PICO_OK != exceptionCode) {
|
|
this->curExceptionCode = exceptionCode;
|
|
va_start(args, (char *)fmt);
|
|
picoos_vSetErrorMsg(this->curExceptionMessage,PICOOS_MAX_EXC_MSG_LEN, exceptionCode, baseMessage, fmt,args);
|
|
PICODBG_DEBUG((
|
|
"exit with exception code=%i, exception message='%s'",
|
|
this->curExceptionCode, this->curExceptionMessage));
|
|
|
|
va_end(args);
|
|
|
|
}
|
|
return this->curExceptionCode;
|
|
}
|
|
|
|
pico_status_t picoos_emGetExceptionCode(picoos_ExceptionManager this)
|
|
{
|
|
return this->curExceptionCode;
|
|
}
|
|
|
|
void picoos_emGetExceptionMessage(picoos_ExceptionManager this, picoos_char * msg, picoos_uint16 maxsize)
|
|
{
|
|
picoos_strlcpy(msg,this->curExceptionMessage,maxsize);
|
|
}
|
|
|
|
void picoos_emRaiseWarning(picoos_ExceptionManager this,
|
|
pico_status_t warningCode, picoos_char * baseMessage, picoos_char * fmt, ...)
|
|
{
|
|
va_list args;
|
|
if ((this->curNumWarnings < PICOOS_MAX_NUM_WARNINGS) && (PICO_OK != warningCode)) {
|
|
if (PICOOS_MAX_NUM_WARNINGS-1 == this->curNumWarnings) {
|
|
this->curWarningCode[this->curNumWarnings] = PICO_EXC_MAX_NUM_EXCEED;
|
|
picoos_strlcpy(this->curWarningMessage[this->curNumWarnings],(picoos_char *) "too many warnings",PICOOS_MAX_WARN_MSG_LEN);
|
|
} else {
|
|
this->curWarningCode[this->curNumWarnings] = warningCode;
|
|
va_start(args, (char *)fmt);
|
|
picoos_vSetErrorMsg(this->curWarningMessage[this->curNumWarnings],PICOOS_MAX_WARN_MSG_LEN, warningCode, baseMessage, fmt,args);
|
|
va_end(args);
|
|
}
|
|
this->curNumWarnings++;
|
|
}
|
|
PICODBG_DEBUG((
|
|
"exit with code=%i and message='%s', resulting in #warnings=%i",
|
|
this->curWarningCode[this->curNumWarnings-1],
|
|
this->curWarningMessage[this->curNumWarnings-1],
|
|
this->curNumWarnings));
|
|
}
|
|
|
|
picoos_uint8 picoos_emGetNumOfWarnings(picoos_ExceptionManager this)
|
|
{
|
|
return this->curNumWarnings;
|
|
}
|
|
|
|
pico_status_t picoos_emGetWarningCode(picoos_ExceptionManager this, picoos_uint8 index)
|
|
{
|
|
if (index < this->curNumWarnings) {
|
|
return this->curWarningCode[index];
|
|
} else {
|
|
return PICO_OK;
|
|
}
|
|
}
|
|
|
|
void picoos_emGetWarningMessage(picoos_ExceptionManager this, picoos_uint8 index, picoos_char * msg, picoos_uint16 maxsize)
|
|
{
|
|
if (index < this->curNumWarnings) {
|
|
picoos_strlcpy(msg,this->curWarningMessage[index],maxsize);
|
|
} else {
|
|
msg[0] = NULLC;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/* *****************************************************************/
|
|
/* File Access */
|
|
/* *****************************************************************/
|
|
|
|
#define picoos_MagicNumber 192837465
|
|
#define picoos_MaxBufSize 1000000
|
|
#define picoos_MaxNrOfBuffers 1000000
|
|
#define picoos_MaxBufLen 8192
|
|
#define picoos_HashFuncId0 0
|
|
#define picoos_HashTableSize0 101
|
|
#define picoos_HashTableSize1 1731
|
|
#define picoos_MaxHashTableSize HashTableSize1
|
|
|
|
#define cardinal_ptr_t picoos_uint32 *
|
|
|
|
typedef struct picoos_buffer
|
|
{
|
|
picoos_char * data;
|
|
int start; /* denotes the file position of the buffer beginning */
|
|
int len; /* denotes the length of the buffer; -1 means invalid buffer */
|
|
int pos; /* denotes the position in the buffer */
|
|
} picoos_buffer_t;
|
|
|
|
typedef picoos_buffer_t picoos_buffer_array_t[picoos_MaxNrOfBuffers];
|
|
typedef picoos_int32 picoos_buffer_index_array_t[picoos_MaxNrOfBuffers];
|
|
|
|
/** object : File
|
|
* shortcut : f
|
|
*
|
|
*/
|
|
typedef struct picoos_file
|
|
{
|
|
picoos_FileName name;
|
|
picoos_uint8 binary;
|
|
picoos_uint8 write;
|
|
|
|
picopal_File nf;
|
|
|
|
picoos_uint32 lFileLen;
|
|
picoos_uint32 lPos;
|
|
|
|
picoos_File next;
|
|
picoos_File prev;
|
|
|
|
} picoos_file_t;
|
|
|
|
picoos_File picoos_newFile(picoos_MemoryManager mm)
|
|
{
|
|
picoos_File this = (picoos_File) picoos_allocate(mm, sizeof(*this));
|
|
if (NULL != this) {
|
|
/* initialize */
|
|
}
|
|
return this;
|
|
}
|
|
|
|
void picoos_disposeFile(picoos_MemoryManager mm, picoos_File * this)
|
|
{
|
|
if (NULL != (*this)) {
|
|
/* terminate */
|
|
picoos_deallocate(mm, (void *)this);
|
|
}
|
|
}
|
|
|
|
|
|
/* ************************************************************
|
|
* low-level file operations
|
|
**************************************************************/
|
|
|
|
static picoos_int32 os_min(const picoos_int32 x, const picoos_int32 y)
|
|
{
|
|
return (x < y) ? x : y;
|
|
}
|
|
|
|
/*
|
|
static picoos_uint8 LReadChar (picoos_File f, picoos_char * ch);
|
|
|
|
|
|
static picoos_uint8 LSetPos (picoos_File f, unsigned int pos);
|
|
|
|
|
|
static picoos_uint8 LGetPos (picoos_File f, picoos_uint32 * pos);
|
|
|
|
|
|
static picoos_uint8 LEof (picoos_File f);
|
|
*/
|
|
|
|
static picoos_bool LOpen(picoos_Common g, picoos_File * f,
|
|
picoos_char fileName[], picopal_access_mode mode)
|
|
{
|
|
picoos_bool done = TRUE;
|
|
|
|
*f = picoos_newFile(g->mm);
|
|
picopal_strcpy((*f)->name, fileName);
|
|
(*f)->write = ((mode == PICOPAL_TEXT_WRITE) || (mode
|
|
== PICOPAL_BINARY_WRITE));
|
|
(*f)->binary = (mode
|
|
== PICOPAL_BINARY_WRITE);
|
|
(*f)->next = NULL;
|
|
(*f)->prev = NULL;
|
|
(*f)->nf = picopal_get_fnil();
|
|
(*f)->lFileLen = 0;
|
|
(*f)->lPos = 0;
|
|
if (picopal_strlen((*f)->name)) {
|
|
(*f)->nf = picopal_fopen((*f)->name, mode);
|
|
done = !(picopal_is_fnil((*f)->nf));
|
|
if (done) {
|
|
(*f)->lFileLen = picopal_flength((*f)->nf);
|
|
}
|
|
}
|
|
if (done) {
|
|
(*f)->next = g->fileList;
|
|
if (g->fileList != NULL) {
|
|
g->fileList->prev = (*f);
|
|
}
|
|
g->fileList = (*f);
|
|
} else {
|
|
picoos_disposeFile(g->mm, f);
|
|
(*f) = NULL;
|
|
}
|
|
return done;
|
|
}
|
|
|
|
static picoos_bool LClose(picoos_Common g, picoos_File * f)
|
|
{
|
|
|
|
picoos_bool done;
|
|
|
|
if (((*f) != NULL)) {
|
|
done = (PICO_OK == picopal_fclose((*f)->nf));
|
|
if (((*f)->next != NULL)) {
|
|
(*f)->next->prev = (*f)->prev;
|
|
}
|
|
if (((*f)->prev != NULL)) {
|
|
(*f)->prev->next = (*f)->next;
|
|
} else {
|
|
g->fileList = (*f)->next;
|
|
}
|
|
picoos_disposeFile(g->mm, f);
|
|
|
|
done = TRUE;
|
|
} else {
|
|
done = FALSE;
|
|
}
|
|
return done;
|
|
|
|
}
|
|
|
|
/* caller must ensure that bytes[] has at least len allocated bytes */
|
|
static picoos_bool LReadBytes(picoos_File f, picoos_uint8 bytes[],
|
|
picoos_uint32 * len)
|
|
{
|
|
picoos_bool done;
|
|
picoos_int32 res;
|
|
|
|
PICODBG_TRACE(("trying to read %i bytes",*len));
|
|
if ((f != NULL)) {
|
|
res = picopal_fread_bytes(f->nf, (void *) &bytes[(0)], 1, (*len));
|
|
PICODBG_TRACE(("res = %i",res));
|
|
if (res < 0) { /* non-ansi */
|
|
(*len) = 0;
|
|
done = FALSE;
|
|
} else if (((picoos_uint32)res != (*len))) {
|
|
(*len) = res;
|
|
done = FALSE;
|
|
} else {
|
|
done = TRUE;
|
|
}
|
|
f->lPos = (f->lPos + (*len));
|
|
} else {
|
|
(*len) = 0;
|
|
done = FALSE;
|
|
}
|
|
return done;
|
|
}
|
|
|
|
static picoos_bool LWriteBytes(picoos_File f, const picoos_char bytes[], int * len) {
|
|
picoos_bool done;
|
|
int res;
|
|
/*int n;
|
|
void * bptr; */
|
|
|
|
if (f != NULL) {
|
|
res = picopal_fwrite_bytes(f->nf, (void *) bytes, 1, *len);
|
|
if ((res < 0)) {
|
|
(*len) = 0;
|
|
done = FALSE;
|
|
} else if ((res != (*len))) {
|
|
(*len) = res;
|
|
done = FALSE;
|
|
} else {
|
|
done = TRUE;
|
|
}
|
|
f->lPos = (f->lPos + (unsigned int) (*len));
|
|
if ((f->lPos > f->lFileLen)) {
|
|
f->lFileLen = f->lPos;
|
|
}
|
|
} else {
|
|
(*len) = 0;
|
|
done = FALSE;
|
|
}
|
|
return done;
|
|
}
|
|
|
|
|
|
static picoos_bool LSetPos(picoos_File f, unsigned int pos)
|
|
{
|
|
|
|
picoos_bool done;
|
|
|
|
if ((f != NULL)) {
|
|
if ((pos == f->lPos)) {
|
|
done = TRUE;
|
|
} else {
|
|
done = (PICO_OK == picopal_fseek(f->nf, pos, PICOPAL_SEEK_SET));
|
|
if (done) {
|
|
f->lPos = pos;
|
|
}
|
|
}
|
|
} else {
|
|
done = FALSE;
|
|
}
|
|
return done;
|
|
}
|
|
|
|
static picoos_bool LGetPos(picoos_File f, picoos_uint32 * pos)
|
|
{
|
|
picoos_bool done = TRUE;
|
|
if ((f != NULL)) {
|
|
(*pos) = f->lPos;
|
|
} else {
|
|
done = FALSE;
|
|
(*pos) = 0;
|
|
}
|
|
return done;
|
|
|
|
}
|
|
|
|
static picoos_bool LEof(picoos_File f)
|
|
{
|
|
picoos_bool isEof;
|
|
|
|
if ((f != NULL)) {
|
|
isEof = picopal_feof(f->nf);
|
|
} else {
|
|
isEof = TRUE;
|
|
}
|
|
return isEof;
|
|
|
|
}
|
|
|
|
/* **************************************************************************************/
|
|
|
|
|
|
|
|
/* read a given string 'str' from file. If no match was found, the read position is advanced until and including the first
|
|
* non-matching character */
|
|
static picoos_bool picoos_StrRead (picoos_File f, picoos_char str[])
|
|
{
|
|
picoos_uint32 i = 0;
|
|
picoos_bool done = TRUE;
|
|
picoos_char b;
|
|
|
|
while (done && (str[i] != NULLC)) {
|
|
done = done && picoos_ReadByte(f,(picoos_char *)&b);
|
|
done = done && (b == str[i]);
|
|
i++;
|
|
}
|
|
return done;
|
|
}
|
|
|
|
/* write 'str' to file */
|
|
static picoos_bool picoos_WriteStr (picoos_File f, picoos_char str[])
|
|
{
|
|
picoos_uint32 i = 0;
|
|
picoos_bool done = TRUE;
|
|
|
|
while (done && (str[i] != NULLC)) {
|
|
done = done && picoos_WriteByte(f,str[i]);
|
|
i++;
|
|
}
|
|
return done;
|
|
}
|
|
|
|
|
|
|
|
/* **** Sequential binary file access ******/
|
|
|
|
/* Remark: 'ReadByte', 'ReadBytes' and 'ReadVar' may be mixed;
|
|
'WriteByte', 'WriteBytes' and 'WriteVar' may be mixed. */
|
|
|
|
/* Open existing binary file for read access. Reading is buffered
|
|
* with 'nrOfBufs' buffers of size 'bufSize'. If 'nrOfBufs' or
|
|
* 'bufSize' is 0 reading is not buffered.
|
|
* If 'key' is not empty, the file is decrypted with 'key'.
|
|
* If the opened file is in an encrypted archive file, it
|
|
*/
|
|
picoos_uint8 picoos_OpenBinary(picoos_Common g, picoos_File * f,
|
|
picoos_char fileName[])
|
|
{
|
|
return LOpen(g, f, fileName, PICOPAL_BINARY_READ);
|
|
}
|
|
|
|
|
|
/* Read next byte from file 'f'. */
|
|
picoos_bool picoos_ReadByte(picoos_File f, picoos_uint8 * by)
|
|
{
|
|
picoos_uint32 n = 1;
|
|
|
|
return picoos_ReadBytes(f, by, &n) && (n == 1);
|
|
|
|
}
|
|
|
|
/* Read next 'len' bytes from 'f' into 'bytes'; 'len' returns the
|
|
number of bytes actually read (may be smaller than requested
|
|
length if at end of file). bytes[] must be big enough to hold at least len bytes.
|
|
*/
|
|
picoos_bool picoos_ReadBytes(picoos_File f, picoos_uint8 bytes[],
|
|
picoos_uint32 * len)
|
|
{
|
|
picoos_bool done = TRUE;
|
|
/* unsigned int origPos; */
|
|
|
|
if ((f != NULL)) {
|
|
done = LReadBytes(f, bytes, len);
|
|
/*if ((f->keyLen > 0)) {
|
|
DecryptBytes(f->key,picoos_MaxKeyLen,f->keyLen,origPos,bytes,(*len));
|
|
}*/
|
|
}
|
|
|
|
return done;
|
|
}
|
|
|
|
|
|
/* Create new binary file.
|
|
If 'key' is not empty, the file is encrypted with 'key'. */
|
|
picoos_bool picoos_CreateBinary(picoos_Common g, picoos_File * f,
|
|
picoos_char fileName[])
|
|
{
|
|
return LOpen(g, f, fileName, PICOPAL_BINARY_WRITE);
|
|
|
|
}
|
|
|
|
|
|
picoos_uint8 picoos_WriteByte(picoos_File f, picoos_char by)
|
|
{
|
|
int n = 1;
|
|
|
|
return picoos_WriteBytes(f, (picoos_char *) &by, &n);
|
|
}
|
|
|
|
|
|
/* Writes 'len' bytes from 'bytes' onto file 'f'; 'len' returns
|
|
the number of bytes actually written. */
|
|
picoos_bool picoos_WriteBytes(picoos_File f, const picoos_char bytes[], picoos_int32 * len) {
|
|
picoos_bool done = FALSE;
|
|
|
|
if (f != NULL) {
|
|
done = LWriteBytes(f, bytes, len);
|
|
}
|
|
|
|
return done;
|
|
}
|
|
|
|
|
|
|
|
/* Close previously opened binary file. */
|
|
picoos_uint8 picoos_CloseBinary(picoos_Common g, picoos_File * f)
|
|
{
|
|
return LClose(g, f);
|
|
|
|
}
|
|
|
|
/* **************************************************************************************/
|
|
/* *** general routines *****/
|
|
|
|
|
|
/* Returns whether end of file was encountered in previous
|
|
read operation. */
|
|
picoos_bool picoos_Eof(picoos_File f)
|
|
{
|
|
if ((NULL != f)) {
|
|
return LEof(f);
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
/* sets the file pointer to
|
|
'pos' bytes from beginning (first byte = byte 0). This
|
|
routine should only be used for binary files. */
|
|
picoos_bool picoos_SetPos(picoos_File f, picoos_int32 pos)
|
|
{
|
|
picoos_bool done = TRUE;
|
|
if ((NULL != f)) {
|
|
done = LSetPos(f, pos);
|
|
} else {
|
|
done = FALSE;
|
|
}
|
|
return done;
|
|
|
|
}
|
|
|
|
/* Get position from file 'f'. */
|
|
picoos_bool picoos_GetPos(picoos_File f, picoos_uint32 * pos)
|
|
{
|
|
if (NULL != f) {
|
|
/* if (f->bFile) {
|
|
(*pos) = BGetPos(f);
|
|
} else { */
|
|
(*pos) = LGetPos(f, pos);
|
|
/* } */
|
|
return TRUE;
|
|
} else {
|
|
(*pos) = 0;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Returns the length of the file in bytes. */
|
|
picoos_bool picoos_FileLength(picoos_File f, picoos_uint32 * len)
|
|
{
|
|
|
|
if (NULL != f) {
|
|
*len = f->lFileLen;
|
|
return TRUE;
|
|
} else {
|
|
*len = 0;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Return full name of file 'f'. maxsize is the size of 'name[]' in bytes */
|
|
picoos_bool picoos_Name(picoos_File f, picoos_char name[], picoos_uint32 maxsize)
|
|
{
|
|
picoos_bool done = TRUE;
|
|
|
|
if (NULL != f) {
|
|
done = (picoos_strlcpy(name, f->name,maxsize) < maxsize);
|
|
} else {
|
|
name[0] = (picoos_char)NULLC;
|
|
done = FALSE;
|
|
}
|
|
|
|
return done;
|
|
}
|
|
|
|
/* Returns whether file 'name' exists or not. */
|
|
picoos_bool picoos_FileExists(picoos_Common g, picoos_char name[])
|
|
{
|
|
picoos_File f;
|
|
|
|
if (picoos_OpenBinary(g, & f,name)) {
|
|
picoos_CloseBinary(g, & f);
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
/* ******************************************************************/
|
|
/* Array conversion operations: all procedures convert 'nrElems' values from
|
|
'src' starting with index 'srcStartInd' into corresponding (possibly
|
|
rounded) values in 'dst' starting at 'dstStartInd'. */
|
|
|
|
/* taking pi to be le, these are just the array versions of read_mem_pi_*int16 */
|
|
typedef picoos_uint8 two_byte_t[2];
|
|
|
|
static void arr_conv_le_int16 (picoos_uint8 src[], picoos_uint32 srcShortStartInd, picoos_uint32 nrElems, picoos_int16 dst[], picoos_uint32 dstStartInd)
|
|
{
|
|
two_byte_t * src_p = (two_byte_t *) (src + (srcShortStartInd * 2));
|
|
picoos_int16 * dst_p = dst + dstStartInd;
|
|
picoos_uint32 i;
|
|
|
|
for (i=0; i<nrElems; i++) {
|
|
*(dst_p++) = (*src_p)[0] + (((*src_p)[1] & 0x7F) << 8) - (((*src_p)[1] & 0x80) ? 0x8000 : 0);
|
|
src_p++;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* convert array of int16 into little-endian format */
|
|
static void arr_conv_int16_le (picoos_int16 src[], picoos_uint32 srcStartInd, picoos_uint32 nrElems, picoos_uint8 dst[], picoos_uint32 dstShortStartInd)
|
|
{
|
|
two_byte_t * dst_p = (two_byte_t *) (dst + (dstShortStartInd * 2));
|
|
picoos_int16 * src_p = src + srcStartInd;
|
|
picoos_uint32 i;
|
|
picoos_uint16 val;
|
|
|
|
for (i=0; i<nrElems; i++) {
|
|
val = (picoos_uint16) *(src_p++);
|
|
(*dst_p)[0] = (picoos_uint8)(val & 0x00FF);
|
|
(*dst_p)[1] = (picoos_uint8)((val & 0xFF00)>>8);
|
|
dst_p++;
|
|
}
|
|
}
|
|
|
|
/* *****************************************************************/
|
|
/* Sampled Data Files */
|
|
/* *****************************************************************/
|
|
|
|
#define PICOOS_SDF_BUF_LEN 1024
|
|
|
|
#define PICOOS_INT16_MIN -32768
|
|
#define PICOOS_INT16_MAX 32767
|
|
#define PICOOS_UINT16_MAX 0xffff
|
|
#define PICOOS_INT32_MIN -2147483648
|
|
#define PICOOS_INT32_MAX 2147483647
|
|
#define PICOOS_UINT32_MAX 0xffffffff
|
|
|
|
/** object : SDFile
|
|
* shortcut : sdf
|
|
*
|
|
*/
|
|
/* typedef struct picoos_sd_file * picoos_SDFile */
|
|
typedef struct picoos_sd_file
|
|
{
|
|
picoos_uint32 sf;
|
|
wave_file_type_t fileType; /* (acoustic) wav, au, raw, other */
|
|
picoos_uint32 hdrSize;
|
|
picoos_encoding_t enc;
|
|
picoos_File file;
|
|
picoos_uint32 nrFileSamples;
|
|
picoos_int16 buf[PICOOS_SDF_BUF_LEN];
|
|
picoos_int32 bufPos;
|
|
picoos_uint8 bBuf[2*PICOOS_SDF_BUF_LEN];
|
|
picoos_bool aborted;
|
|
} picoos_sd_file_t;
|
|
|
|
|
|
/* Tries to read wav header at beginning of file 'f';
|
|
returns sampling rate 'sf', encoding type 'enc',
|
|
nr of samples in file 'nrSamples', header size 'hdrSize',
|
|
and byte order 'bOrder'; returns whether a supported
|
|
wav header and format was found. */
|
|
static picoos_bool picoos_readWavHeader(picoos_File f, picoos_uint32 * sf,
|
|
picoos_encoding_t * enc, picoos_uint32 * nrSamples,
|
|
picoos_uint32 * hdrSize) {
|
|
picoos_uint16 n16;
|
|
picoos_uint32 n32;
|
|
picoos_uint16 formatTag;
|
|
picoos_uint32 sampleRate;
|
|
picoos_uint32 bytesPerSec;
|
|
picoos_uint16 blockAlign;
|
|
picoos_uint16 sampleSize;
|
|
picoos_uint32 dataLength;
|
|
picoos_uint32 fileLen;
|
|
picoos_uint32 nrFileSamples;
|
|
picoos_bool done;
|
|
|
|
|
|
picoos_SetPos(f, 0);
|
|
picoos_FileLength(f, &fileLen);
|
|
done = picoos_StrRead(f, (picoos_char *) "RIFF");
|
|
done = done && (PICO_OK == picoos_read_le_uint32(f, &n32)); /* length of riff chunk, unused */
|
|
done = done && picoos_StrRead(f, (picoos_char *) "WAVE");
|
|
done = done && picoos_StrRead(f, (picoos_char *) "fmt ");
|
|
done = done && (PICO_OK == picoos_read_le_uint32(f, &n32)); /* length of fmt chunk in bytes; must be 16 */
|
|
done = done && (n32 == 16);
|
|
done = done && (PICO_OK == picoos_read_le_uint16(f, &formatTag));
|
|
done = done && (PICO_OK == picoos_read_le_uint16(f, &n16)); /* number of channels; must be mono */
|
|
done = done && (n16 == 1);
|
|
done = done && (PICO_OK == picoos_read_le_uint32(f, &sampleRate));
|
|
done = done && (PICO_OK == picoos_read_le_uint32(f, &bytesPerSec));
|
|
done = done && (PICO_OK == picoos_read_le_uint16(f, &blockAlign));
|
|
done = done && (PICO_OK == picoos_read_le_uint16(f, &sampleSize));
|
|
done = done && picoos_StrRead(f, (picoos_char *) "data");
|
|
done = done && (PICO_OK == picoos_read_le_uint32(f, &dataLength)); /* length of data chunk in bytes */
|
|
(*hdrSize) = 44;
|
|
if (done) {
|
|
(*sf) = sampleRate;
|
|
(*nrSamples) = 0;
|
|
switch (formatTag) {
|
|
case FORMAT_TAG_LIN:
|
|
(*enc) = PICOOS_ENC_LIN;
|
|
done = ((blockAlign == 2) && (sampleSize == 16));
|
|
(*nrSamples) = (dataLength / 2);
|
|
nrFileSamples = ((fileLen - (*hdrSize)) / 2);
|
|
break;
|
|
case FORMAT_TAG_ULAW:
|
|
(*enc) = PICOOS_ENC_ULAW;
|
|
done = ((blockAlign == 1) && (sampleSize == 8));
|
|
(*nrSamples) = dataLength;
|
|
nrFileSamples = (fileLen - (*hdrSize));
|
|
break;
|
|
case FORMAT_TAG_ALAW:
|
|
(*enc) = PICOOS_ENC_ALAW;
|
|
done = ((blockAlign == 1) && (sampleSize == 8));
|
|
(*nrSamples) = dataLength;
|
|
nrFileSamples = (fileLen - (*hdrSize));
|
|
break;
|
|
default:
|
|
done = FALSE;
|
|
break;
|
|
}
|
|
if (!done) {
|
|
/* communicate "unsupported format" */
|
|
PICODBG_WARN(("unsupported wav format"));
|
|
} else {
|
|
if (nrFileSamples != (*nrSamples)) {
|
|
/* warn "inconsistent number of samples" */
|
|
PICODBG_WARN(("inconsistent number of samples in wav file: %d vs. %d",nrFileSamples,(*nrSamples)));
|
|
(*nrSamples) = nrFileSamples;
|
|
}
|
|
}
|
|
}
|
|
return done;
|
|
}
|
|
|
|
|
|
|
|
extern picoos_bool picoos_sdfOpenIn(picoos_Common g, picoos_SDFile * sdFile,
|
|
picoos_char fileName[], picoos_uint32 * sf, picoos_encoding_t * enc,
|
|
picoos_uint32 * numSamples)
|
|
{
|
|
picoos_bool done = FALSE;
|
|
picoos_sd_file_t * sdf = NULL;
|
|
wave_file_type_t fileType = FILE_TYPE_OTHER;
|
|
|
|
(*sf) = 0;
|
|
(*numSamples) = 0;
|
|
(*enc) = PICOOS_ENC_LIN;
|
|
(*sdFile) = NULL;
|
|
|
|
sdf = picoos_allocate(g->mm,sizeof(picoos_sd_file_t));
|
|
if (NULL == sdf) {
|
|
picoos_emRaiseWarning(g->em,PICO_EXC_OUT_OF_MEM,NULL,NULL);
|
|
return FALSE;
|
|
}
|
|
|
|
/* buffered access not supported, yet */
|
|
if (picoos_OpenBinary(g,&(sdf->file),fileName)) {
|
|
if (picoos_has_extension(fileName,(picoos_char *) ".wav")) {
|
|
fileType = FILE_TYPE_WAV;
|
|
done = picoos_readWavHeader(sdf->file,&(sdf->sf),&(sdf->enc),&(sdf->nrFileSamples),&(sdf->hdrSize));
|
|
} else {
|
|
/* we prefer not to treat other formats, rather than treat it as raw */
|
|
/* fileType = FILE_TYPE_RAW; */
|
|
fileType = FILE_TYPE_OTHER;
|
|
done = FALSE;
|
|
}
|
|
|
|
if (FILE_TYPE_OTHER == fileType) {
|
|
picoos_emRaiseWarning(g->em,PICO_EXC_UNEXPECTED_FILE_TYPE,(picoos_char *)"unsupported filename suffix",NULL);
|
|
} else if (!done) {
|
|
picoos_emRaiseWarning(g->em,PICO_EXC_UNEXPECTED_FILE_TYPE,(picoos_char *)"non-conforming header",NULL);
|
|
} else {
|
|
(*numSamples) = sdf->nrFileSamples;
|
|
(*sf) = sdf->sf;
|
|
(*enc) = sdf->enc;
|
|
/* check whether sd file properties are supported */
|
|
if (PICOOS_ENC_LIN != sdf->enc) {
|
|
done = FALSE;
|
|
picoos_emRaiseWarning(g->em,PICO_EXC_UNEXPECTED_FILE_TYPE,NULL,(picoos_char *)"encoding not supported");
|
|
}
|
|
if (SAMPLE_FREQ_16KHZ != sdf->sf) {
|
|
done = FALSE;
|
|
picoos_emRaiseWarning(g->em,PICO_EXC_UNEXPECTED_FILE_TYPE,NULL,(picoos_char *)"sample frequency not supported");
|
|
}
|
|
(*sdFile) = sdf;
|
|
}
|
|
if (!done){
|
|
picoos_CloseBinary(g,&(sdf->file));
|
|
}
|
|
} else {
|
|
picoos_emRaiseException(g->em,PICO_EXC_CANT_OPEN_FILE,NULL,NULL);
|
|
}
|
|
if (!done) {
|
|
picoos_deallocate(g->mm,(void *)&sdf);
|
|
(*sdFile) = NULL;
|
|
}
|
|
return done;
|
|
}
|
|
|
|
|
|
static void picoos_sdfLoadSamples(picoos_SDFile sdFile,
|
|
picoos_uint32 * nrSamples) {
|
|
picoos_uint32 len;
|
|
picoos_sd_file_t * sdf = sdFile;
|
|
|
|
switch (sdFile->enc) {
|
|
case PICOOS_ENC_LIN:
|
|
if ((*nrSamples) > PICOOS_SDF_BUF_LEN) {
|
|
(*nrSamples) = PICOOS_SDF_BUF_LEN;
|
|
}
|
|
len = 2 * (*nrSamples);
|
|
picoos_ReadBytes(sdf->file, sdf->bBuf, &len);
|
|
(*nrSamples) = len / 2;
|
|
arr_conv_le_int16(sdf->bBuf, 0, (*nrSamples), sdf->buf, 0);
|
|
break;
|
|
/* @todo : may be useful */
|
|
case PICOOS_ENC_ULAW:
|
|
case PICOOS_ENC_ALAW:
|
|
default:
|
|
(*nrSamples) = 0;
|
|
}
|
|
|
|
}
|
|
|
|
extern picoos_bool picoos_sdfGetSamples (
|
|
picoos_SDFile sdFile,
|
|
picoos_uint32 start,
|
|
picoos_uint32 * nrSamples,
|
|
picoos_int16 samples[])
|
|
{
|
|
picoos_uint32 b;
|
|
picoos_uint32 rem;
|
|
picoos_uint32 n;
|
|
picoos_uint32 i;
|
|
picoos_uint32 j;
|
|
picoos_bool done = FALSE;
|
|
|
|
if (NULL == sdFile) {
|
|
(*nrSamples) = 0;
|
|
} else {
|
|
if (start >= sdFile->nrFileSamples) {
|
|
if (start > sdFile->nrFileSamples) {
|
|
PICODBG_WARN(("start has to be <= sdFile->nrFileSamples"));
|
|
}
|
|
(*nrSamples) = 0;
|
|
} else {
|
|
if (((start + (*nrSamples)) > sdFile->nrFileSamples)) {
|
|
(*nrSamples) = (sdFile->nrFileSamples - start);
|
|
}
|
|
if ((sdFile->enc == PICOOS_ENC_LIN)) {
|
|
b = 2;
|
|
} else {
|
|
b = 1;
|
|
}
|
|
picoos_SetPos(sdFile->file,(sdFile->hdrSize + (b * start)));
|
|
j = 0;
|
|
rem = (*nrSamples);
|
|
n = rem;
|
|
while ((rem > 0) && (n > 0)) {
|
|
/* set n=min(rem,buffer_length) and try loading next n samples */
|
|
n = (rem < PICOOS_SDF_BUF_LEN) ? rem : PICOOS_SDF_BUF_LEN;
|
|
picoos_sdfLoadSamples(sdFile, &n);
|
|
/* n may be smaller now */
|
|
for (i = 0; i < n; i++) {
|
|
samples[j] = sdFile->buf[i];
|
|
j++;
|
|
}
|
|
rem -= n;
|
|
start += n;
|
|
}
|
|
(*nrSamples) = j;
|
|
done = ((*nrSamples) > 0);
|
|
}
|
|
}
|
|
return done;
|
|
}
|
|
|
|
|
|
extern picoos_bool picoos_sdfCloseIn (picoos_Common g, picoos_SDFile * sdFile)
|
|
{
|
|
if (NULL != (*sdFile)) {
|
|
picoos_CloseBinary(g,&((*sdFile)->file));
|
|
picoos_deallocate(g->mm,(void *)sdFile);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static picoos_bool picoos_writeWavHeader(picoos_File f, picoos_uint32 sf,
|
|
picoos_encoding_t enc, picoos_uint32 nrSamples,
|
|
picoos_uint32 * hdrSize) {
|
|
picoos_uint16 formatTag = FORMAT_TAG_LIN;
|
|
picoos_uint32 sampleRate;
|
|
picoos_uint32 bytesPerSec;
|
|
picoos_uint32 bytesPerSample = 2;
|
|
picoos_uint16 blockAlign;
|
|
picoos_uint16 sampleSize = 16;
|
|
picoos_uint32 dataLength;
|
|
picoos_bool done = TRUE;
|
|
|
|
picoos_SetPos(f, 0);
|
|
|
|
switch (enc) {
|
|
case PICOOS_ENC_LIN:
|
|
formatTag = FORMAT_TAG_LIN;
|
|
bytesPerSample = 2;
|
|
sampleSize = 16;
|
|
break;
|
|
case PICOOS_ENC_ULAW:
|
|
formatTag = FORMAT_TAG_ULAW;
|
|
bytesPerSample = 1;
|
|
sampleSize = 8;
|
|
break;
|
|
case PICOOS_ENC_ALAW:
|
|
formatTag = FORMAT_TAG_ALAW;
|
|
bytesPerSample = 1;
|
|
sampleSize = 8;
|
|
break;
|
|
default:
|
|
done = FALSE;
|
|
break;
|
|
}
|
|
|
|
bytesPerSec = (sf * bytesPerSample);
|
|
blockAlign = bytesPerSample;
|
|
sampleRate = sf;
|
|
dataLength = (bytesPerSample * nrSamples);
|
|
done = done && picoos_WriteStr(f,(picoos_char *)"RIFF");
|
|
done = done && picoos_write_le_uint32(f,dataLength + 36);
|
|
done = done && picoos_WriteStr(f,(picoos_char *)"WAVE");
|
|
done = done && picoos_WriteStr(f,(picoos_char *)"fmt ");
|
|
done = done && picoos_write_le_uint32(f,16);
|
|
done = done && picoos_write_le_uint16(f,formatTag);
|
|
done = done && picoos_write_le_uint16(f,1);
|
|
done = done && picoos_write_le_uint32(f,sampleRate);
|
|
done = done && picoos_write_le_uint32(f,bytesPerSec);
|
|
done = done && picoos_write_le_uint16(f,blockAlign);
|
|
done = done && picoos_write_le_uint16(f,sampleSize);
|
|
done = done && picoos_WriteStr(f,(picoos_char *)"data");
|
|
done = done && picoos_write_le_uint32(f,dataLength);
|
|
(*hdrSize) = 44;
|
|
return done;
|
|
}
|
|
|
|
|
|
#define DummyLen 100000000
|
|
|
|
extern picoos_bool picoos_sdfOpenOut(picoos_Common g, picoos_SDFile * sdFile,
|
|
picoos_char fileName[], int sf, picoos_encoding_t enc)
|
|
{
|
|
picoos_bool done = TRUE;
|
|
picoos_sd_file_t * sdf = NULL;
|
|
|
|
(*sdFile) = NULL;
|
|
sdf = picoos_allocate(g->mm, sizeof(picoos_sd_file_t));
|
|
if (NULL == sdf) {
|
|
picoos_emRaiseWarning(g->em, PICO_EXC_OUT_OF_MEM, NULL, NULL);
|
|
return FALSE;
|
|
}
|
|
sdf->sf = sf;
|
|
sdf->enc = enc;
|
|
/* check whether sd file properties are supported */
|
|
if (PICOOS_ENC_LIN != sdf->enc) {
|
|
done = FALSE;
|
|
picoos_emRaiseWarning(g->em, PICO_EXC_UNEXPECTED_FILE_TYPE, NULL,
|
|
(picoos_char *) "encoding not supported");
|
|
}
|
|
if (SAMPLE_FREQ_16KHZ != sdf->sf) {
|
|
done = FALSE;
|
|
picoos_emRaiseWarning(g->em, PICO_EXC_UNEXPECTED_FILE_TYPE, NULL,
|
|
(picoos_char *) "sample frequency not supported");
|
|
}
|
|
if (done) {
|
|
sdf->nrFileSamples = 0;
|
|
sdf->bufPos = 0;
|
|
sdf->aborted = FALSE;
|
|
if (picoos_CreateBinary(g, &(sdf->file), fileName)) {
|
|
if (picoos_has_extension(fileName, (picoos_char *) ".wav")) {
|
|
sdf->fileType = FILE_TYPE_WAV;
|
|
done = picoos_writeWavHeader(sdf->file, sdf->sf, sdf->enc,
|
|
DummyLen, &(sdf->hdrSize));
|
|
} else {
|
|
/* we prefer not to treat other formats, rather than treat it as raw */
|
|
/* fileType = FILE_TYPE_RAW; */
|
|
sdf->fileType = FILE_TYPE_OTHER;
|
|
done = FALSE;
|
|
}
|
|
|
|
if (FILE_TYPE_OTHER == sdf->fileType) {
|
|
picoos_emRaiseWarning(g->em, PICO_EXC_UNEXPECTED_FILE_TYPE,
|
|
(picoos_char *) "unsupported filename suffix", NULL);
|
|
} else if (!done) {
|
|
picoos_emRaiseWarning(g->em, PICO_EXC_UNEXPECTED_FILE_TYPE,
|
|
(picoos_char *) "non-conforming header", NULL);
|
|
} else {
|
|
(*sdFile) = sdf;
|
|
}
|
|
if (!done) {
|
|
picoos_CloseBinary(g, &(sdf->file));
|
|
}
|
|
} else {
|
|
picoos_emRaiseException(g->em, PICO_EXC_CANT_OPEN_FILE, NULL, NULL);
|
|
}
|
|
}
|
|
if (!done) {
|
|
picoos_deallocate(g->mm, (void *) &sdf);
|
|
(*sdFile) = NULL;
|
|
}
|
|
return done;
|
|
}
|
|
|
|
static picoos_bool picoos_sdfFlushOutBuf(picoos_SDFile sdFile)
|
|
{
|
|
picoos_bool done = FALSE;
|
|
picoos_int32 len;
|
|
picoos_int32 nrSamples;
|
|
|
|
if (!(sdFile->aborted)) {
|
|
nrSamples = sdFile->bufPos;
|
|
switch (sdFile->enc) {
|
|
case PICOOS_ENC_LIN:
|
|
arr_conv_int16_le(sdFile->buf, 0, nrSamples, sdFile->bBuf, 0);
|
|
len = (nrSamples * 2);
|
|
done = picoos_WriteBytes(sdFile->file, sdFile->bBuf, &len)
|
|
&& ((nrSamples * 2) == len);
|
|
break;
|
|
case PICOOS_ENC_ULAW:
|
|
case PICOOS_ENC_ALAW:
|
|
default:
|
|
nrSamples = 0;
|
|
break;
|
|
}
|
|
sdFile->nrFileSamples = (sdFile->nrFileSamples + nrSamples);
|
|
}
|
|
|
|
sdFile->bufPos = 0;
|
|
return done;
|
|
}
|
|
|
|
extern picoos_bool picoos_sdfFlushOutput(picoos_SDFile sdFile)
|
|
{
|
|
if ((sdFile != NULL) && !(sdFile->aborted) && (sdFile->bufPos > 0)) {
|
|
return picoos_sdfFlushOutBuf(sdFile);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
extern picoos_bool picoos_sdfPutSamples (picoos_SDFile sdFile, picoos_uint32 nrSamples, picoos_int16 samples[])
|
|
{
|
|
picoos_uint32 i;
|
|
picoos_int32 s;
|
|
picoos_bool done = FALSE;
|
|
|
|
if ((sdFile != NULL) && !(sdFile->aborted)) {
|
|
done = TRUE;
|
|
for (i = 0; i < nrSamples; i++) {
|
|
s = samples[i];
|
|
if ((s > PICOOS_INT16_MAX)) {
|
|
s = PICOOS_INT16_MAX;
|
|
} else if (s < PICOOS_INT16_MIN) {
|
|
s = PICOOS_INT16_MIN;
|
|
}
|
|
sdFile->buf[sdFile->bufPos++] = s;
|
|
if (sdFile->bufPos >= PICOOS_SDF_BUF_LEN) {
|
|
done = picoos_sdfFlushOutBuf(sdFile);
|
|
}
|
|
}
|
|
} else {
|
|
done = FALSE;
|
|
}
|
|
return done;
|
|
}
|
|
|
|
|
|
extern picoos_bool picoos_sdfCloseOut (picoos_Common g, picoos_SDFile * sdFile)
|
|
{
|
|
|
|
picoos_bool done = TRUE;
|
|
picoos_uint32 hdrSize;
|
|
|
|
if (NULL != (*sdFile)) {
|
|
if (!((*sdFile)->aborted) && ((*sdFile)->bufPos > 0)) {
|
|
done = picoos_sdfFlushOutBuf(*sdFile);
|
|
}
|
|
if (FILE_TYPE_WAV == (*sdFile)->fileType) {
|
|
done = picoos_writeWavHeader((*sdFile)->file, (*sdFile)->sf,
|
|
(*sdFile)->enc, (*sdFile)->nrFileSamples, &hdrSize);
|
|
}
|
|
done = picoos_CloseBinary(g, &((*sdFile)->file));
|
|
picoos_deallocate(g->mm, (void *) sdFile);
|
|
}
|
|
return done;
|
|
}
|
|
|
|
|
|
/* *****************************************************************/
|
|
/* FileHeader */
|
|
/* *****************************************************************/
|
|
|
|
|
|
|
|
pico_status_t picoos_clearHeader(picoos_FileHeader header)
|
|
{
|
|
picoos_uint8 i;
|
|
for (i=0; i < PICOOS_MAX_NUM_HEADER_FIELDS; i++) {
|
|
header->field[i].key[0] = NULLC;
|
|
header->field[i].value[0] = NULLC;
|
|
header->field[i].op = PICOOS_FIELD_IGNORE;
|
|
}
|
|
header->numFields = 0;
|
|
return PICO_OK;
|
|
}
|
|
|
|
pico_status_t picoos_setHeaderField(picoos_FileHeader header,
|
|
picoos_uint8 index, picoos_char * key, picoos_char * value,
|
|
picoos_compare_op_t op)
|
|
{
|
|
if (index >= header->numFields) {
|
|
return PICO_ERR_INDEX_OUT_OF_RANGE;
|
|
}
|
|
header->field[index].op = op;
|
|
if ((picoos_strlcpy(header->field[index].key, key,
|
|
PICOOS_MAX_FIELD_STRING_LEN) < PICOOS_MAX_FIELD_STRING_LEN)
|
|
&& (picoos_strlcpy(header->field[index].value, value,
|
|
PICOOS_MAX_FIELD_STRING_LEN)) < PICOOS_MAX_FIELD_STRING_LEN) {
|
|
return PICO_OK;
|
|
} else {
|
|
return PICO_ERR_INDEX_OUT_OF_RANGE;
|
|
}
|
|
}
|
|
|
|
|
|
/* caller has to make sure allocated space at key and value are large enough to hold a picoos_field_string */
|
|
pico_status_t picoos_getHeaderField(picoos_FileHeader header, picoos_uint8 index, picoos_field_string_t key, picoos_field_string_t value, picoos_compare_op_t * op)
|
|
{
|
|
if (index >= header->numFields) {
|
|
return PICO_ERR_INDEX_OUT_OF_RANGE;
|
|
}
|
|
*op = header->field[index].op;
|
|
if ((picoos_strlcpy(key,header->field[index].key,
|
|
PICOOS_MAX_FIELD_STRING_LEN) < PICOOS_MAX_FIELD_STRING_LEN)
|
|
&& (picoos_strlcpy(value,header->field[index].value,
|
|
PICOOS_MAX_FIELD_STRING_LEN)) < PICOOS_MAX_FIELD_STRING_LEN) {
|
|
return PICO_OK;
|
|
} else {
|
|
return PICO_ERR_INDEX_OUT_OF_RANGE;
|
|
}
|
|
return PICO_OK;
|
|
}
|
|
|
|
|
|
/* check whether 'str' of length strlen matches contents in circular buffer buf, located in the first strlen bytes and ending
|
|
* position bufpos. */
|
|
static picoos_uint8 os_matched( picoos_char * str, picoos_uint32 strlen, picoos_char * buf, picoos_int32 bufpos) {
|
|
picoos_int32 i = strlen-1;
|
|
while (i >= 0 && buf[bufpos] == str[i]) {
|
|
i--;
|
|
bufpos--;
|
|
if (bufpos < 0) {
|
|
bufpos = strlen-1;
|
|
}
|
|
}
|
|
return (i<0);
|
|
}
|
|
|
|
pico_status_t picoos_getSVOXHeaderString(picoos_char * str, picoos_uint8 * len, picoos_uint32 maxlen)
|
|
{
|
|
picoos_char * ch;
|
|
*len = picoos_strlcpy(str,picoos_SVOXFileHeader,maxlen);
|
|
if (*len < maxlen) {
|
|
ch = str;
|
|
/* SVOX header is made less readable */
|
|
while (*ch) {
|
|
*ch -= ' ';
|
|
ch++;
|
|
}
|
|
return PICO_OK;
|
|
} else {
|
|
return PICO_ERR_OTHER;
|
|
}
|
|
}
|
|
|
|
pico_status_t picoos_readPicoHeader(picoos_File f, picoos_uint32 * headerlen)
|
|
{
|
|
picoos_char str[32];
|
|
picoos_char buf[32];
|
|
picoos_uint8 strlen, bufpos;
|
|
picoos_uint32 n;
|
|
picoos_uint8 done;
|
|
|
|
picoos_getSVOXHeaderString(str,&strlen,32);
|
|
/* search for svox header somewhere near the file start. This allows for initial
|
|
* non-svox-header bytes for a customer-specific header and/or filling bytes for alignment */
|
|
*headerlen = 0;
|
|
/* read in initial chunk of length strlen */
|
|
n = strlen;
|
|
done = picoos_ReadBytes(f,(picoos_uint8 *)buf,&n) && (n == strlen);
|
|
if (done) {
|
|
*headerlen = n;
|
|
bufpos = strlen-1; /* last legal buf position */
|
|
done = os_matched(str,strlen,buf,bufpos);
|
|
while (!done && *headerlen < PICO_MAX_FOREIGN_HEADER_LEN) {
|
|
n = 1;
|
|
bufpos = (bufpos + 1) % strlen;
|
|
done = picoos_ReadBytes(f,(picoos_uint8 *)buf+bufpos,&n) && 1 == n;
|
|
done = done && os_matched(str,strlen,buf,bufpos);
|
|
headerlen++;
|
|
}
|
|
}
|
|
if (done) {
|
|
return PICO_OK;
|
|
} else {
|
|
return PICO_EXC_UNEXPECTED_FILE_TYPE;
|
|
}
|
|
}
|
|
|
|
picoos_uint8 picoos_get_str (picoos_char * fromStr, picoos_uint32 * pos, picoos_char * toStr, picoos_objsize_t maxsize)
|
|
{
|
|
picoos_uint8 i = 0;
|
|
/* skip non-printables */
|
|
|
|
while ((fromStr[*pos] != NULLC) && (fromStr[*pos] <= ' ')) {
|
|
(*pos)++;
|
|
}
|
|
/* copy printable portion */
|
|
while ((fromStr[*pos] != NULLC) && (fromStr[*pos] > ' ') && (i < maxsize-1)) {
|
|
toStr[i++] = fromStr[(*pos)++];
|
|
}
|
|
toStr[i] = NULLC;
|
|
return (i > 0) && (fromStr[*pos] <= ' ');
|
|
}
|
|
|
|
pico_status_t picoos_hdrParseHeader(picoos_FileHeader header, picoos_header_string_t str)
|
|
{
|
|
picoos_uint32 curpos = 0;
|
|
picoos_uint8 i, numFields;
|
|
|
|
|
|
/* read number of fields */
|
|
numFields = str[curpos++];
|
|
numFields = os_min(numFields,PICOOS_MAX_NUM_HEADER_FIELDS);
|
|
/* read in all field pairs */
|
|
PICODBG_DEBUG(("number of fields = %i", numFields));
|
|
for (i = 0; i < numFields; i++) {
|
|
picoos_get_str(str,&curpos,header->field[i].key,PICOOS_MAX_FIELD_STRING_LEN);
|
|
picoos_get_str(str,&curpos,header->field[i].value,PICOOS_MAX_FIELD_STRING_LEN);
|
|
}
|
|
return PICO_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* **************************************************************************/
|
|
/* Read little-endian / platform-independent integers from file or memory */
|
|
/* **************************************************************************/
|
|
|
|
/* read little-endian */
|
|
pico_status_t picoos_read_le_uint16 (picoos_File file, picoos_uint16 * val)
|
|
{
|
|
picoos_uint8 by[2];
|
|
picoos_uint32 n = 2;
|
|
if (picoos_ReadBytes(file, by, &n) && 2 == n) {
|
|
/* little-endian */
|
|
*val = (picoos_uint16) by[1] << 8 | (picoos_uint16) by[0];
|
|
return PICO_OK;
|
|
} else {
|
|
*val = 0;
|
|
return PICO_ERR_OTHER;
|
|
}
|
|
}
|
|
|
|
pico_status_t picoos_read_le_int16 (picoos_File file, picoos_int16 * val)
|
|
{
|
|
return picoos_read_le_uint16(file, (picoos_uint16 *)val);
|
|
}
|
|
|
|
pico_status_t picoos_read_le_uint32 (picoos_File file, picoos_uint32 * val)
|
|
{
|
|
picoos_uint8 by[4];
|
|
picoos_uint32 n = 4;
|
|
if (picoos_ReadBytes(file, by, &n) && (4 == n)) {
|
|
/* little-endian */
|
|
PICODBG_TRACE(("reading uint 32: %i %i %i %i",
|
|
by[0], by[1], by[2], by[3]));
|
|
*val = (((picoos_uint32) by[3] << 8 | (picoos_uint32) by[2]) << 8 | (picoos_uint32) by[1]) << 8 | (picoos_uint32) by[0];
|
|
PICODBG_TRACE(("uint 32: %i %i %i %i corresponds %i",
|
|
by[0], by[1], by[2], by[3], *val));
|
|
return PICO_OK;
|
|
} else {
|
|
*val = 0;
|
|
return PICO_ERR_OTHER;
|
|
}
|
|
}
|
|
|
|
/* platform-independent */
|
|
/* our convention is that pi is little-endian. */
|
|
|
|
/** @todo : direct implementation if too slow */
|
|
|
|
pico_status_t picoos_read_pi_uint16 (picoos_File file, picoos_uint16 * val)
|
|
{
|
|
return picoos_read_le_uint16(file,val);
|
|
}
|
|
|
|
pico_status_t picoos_read_pi_uint32 (picoos_File file, picoos_uint32 * val)
|
|
{
|
|
return picoos_read_le_uint32(file, val);
|
|
}
|
|
|
|
pico_status_t picoos_read_pi_int32 (picoos_File file, picoos_int32 * val)
|
|
{
|
|
return picoos_read_le_uint32(file, (picoos_uint32 *)val);
|
|
}
|
|
|
|
/* read pi from memory */
|
|
|
|
pico_status_t picoos_read_mem_pi_uint16 (picoos_uint8 * data, picoos_uint32 * pos, picoos_uint16 * val)
|
|
{
|
|
picoos_uint8 * by = data + *pos;
|
|
|
|
/* little-endian */
|
|
*val = (picoos_uint16) by[1] << 8 | (picoos_uint16) by[0];
|
|
(*pos) += 2;
|
|
return PICO_OK;
|
|
}
|
|
|
|
pico_status_t picoos_read_mem_pi_uint32 (picoos_uint8 * data, picoos_uint32 * pos, picoos_uint32 * val)
|
|
{
|
|
picoos_uint8 * by = data + *pos;
|
|
|
|
/* little-endian */
|
|
*val = (((picoos_uint32) by[3] << 8 | (picoos_uint32) by[2]) << 8 | (picoos_uint32) by[1]) << 8 | (picoos_uint32) by[0];
|
|
(*pos) += 4;
|
|
return PICO_OK;
|
|
}
|
|
|
|
/* **************************************************************************/
|
|
/* Write little-endian / platform-independent integers into file or memory */
|
|
/* **************************************************************************/
|
|
/* write little-endian */
|
|
pico_status_t picoos_write_le_uint16 (picoos_File file, picoos_uint16 val)
|
|
{
|
|
picoos_int32 len = 2;
|
|
picoos_uint8 by[2];
|
|
|
|
by[0] = (picoos_uint8)((val) & 0x00FF);
|
|
by[1] = (picoos_uint8)(((val) & 0xFF00)>>8);
|
|
return (picoos_WriteBytes(file,by,&len) && (2 == len));
|
|
}
|
|
pico_status_t picoos_write_le_uint32 (picoos_File file, picoos_uint32 val)
|
|
{
|
|
picoos_int32 len = 4;
|
|
picoos_uint8 by[4];
|
|
|
|
by[0] = (picoos_uint8)(val & 0x000000FF);
|
|
by[1] = (picoos_uint8)((val & 0x0000FF00)>>8);
|
|
by[2] = (picoos_uint8)((val & 0x00FF0000)>>16);
|
|
by[3] = (picoos_uint8)((val & 0xFF000000)>>24);
|
|
return (picoos_WriteBytes(file,by,&len) && (4 == len));
|
|
}
|
|
|
|
/* write pi to mem */
|
|
pico_status_t picoos_write_mem_pi_uint16 (picoos_uint8 * data, picoos_uint32 * pos, picoos_uint16 val)
|
|
{
|
|
picoos_uint8 * by = data + *pos;
|
|
/* little-endian */
|
|
by[0] = (picoos_uint8)((val) & 0x00FF);
|
|
by[1] = (picoos_uint8)(((val) & 0xFF00)>>8);
|
|
(*pos) += 2;
|
|
return PICO_OK;
|
|
}
|
|
|
|
/* *****************************************************************/
|
|
/* String search and compare operations */
|
|
/* *****************************************************************/
|
|
|
|
/* this function is case-sensitive */
|
|
picoos_uint8 picoos_has_extension(const picoos_char *str, const picoos_char *suf)
|
|
{
|
|
picoos_int32 istr = picoos_strlen(str)-1;
|
|
picoos_int32 isuf = picoos_strlen(suf)-1;
|
|
while ((istr >= 0) && (isuf >=0) && (str[istr] == suf[isuf])) {
|
|
istr--;
|
|
isuf--;
|
|
}
|
|
return (isuf < 0);
|
|
}
|
|
|
|
|
|
/* *****************************************************************/
|
|
/* String/Number Conversions (may be moved to picopal) */
|
|
/* *****************************************************************/
|
|
|
|
pico_status_t picoos_string_to_int32(picoos_char str[],
|
|
picoos_int32 * res)
|
|
{
|
|
/* syntax: [+|-] dig {dig} */
|
|
|
|
int i;
|
|
int neg;
|
|
int val;
|
|
int err;
|
|
|
|
err = 0;
|
|
i = 0;
|
|
while ((str[i] <= ' ') && (str[i] != '\0')) {
|
|
i++;
|
|
}
|
|
neg = 0;
|
|
if (str[i] == '-') {
|
|
neg = 1;
|
|
i++;
|
|
} else if (str[i] == '+') {
|
|
i++;
|
|
}
|
|
val = 0;
|
|
if ((str[i] < '0') || (str[i]> '9')) {
|
|
err = 1;
|
|
}
|
|
while ((str[i] >= '0') && (str[i] <= '9')) {
|
|
val = val * 10 + (str[i] - '0');
|
|
i++;
|
|
}
|
|
while ((str[i] <= ' ') && (str[i] != '\0')) {
|
|
i++;
|
|
}
|
|
if (neg == 1) {
|
|
val = -val;
|
|
}
|
|
if ((err == 0) && (str[i] == '\0')) {
|
|
(*res) = val;
|
|
return PICO_OK;
|
|
} else {
|
|
(*res) = 0;
|
|
return PICO_EXC_NUMBER_FORMAT;
|
|
}
|
|
}
|
|
|
|
pico_status_t picoos_string_to_uint32(picoos_char str[],
|
|
picoos_uint32 * res)
|
|
{
|
|
/* syntax: [+] dig {dig} */
|
|
|
|
int i;
|
|
int val;
|
|
int err;
|
|
|
|
err = 0;
|
|
i = 0;
|
|
while ((str[i] <= ' ') && (str[i] != '\0')) {
|
|
i++;
|
|
}
|
|
if (str[i] == '+') {
|
|
i++;
|
|
}
|
|
val = 0;
|
|
if ((str[i] < '0') || (str[i]> '9')) {
|
|
err = 1;
|
|
}
|
|
while ((str[i] >= '0') && (str[i] <= '9')) {
|
|
val = val * 10 + (str[i] - '0');
|
|
i++;
|
|
}
|
|
while ((str[i] <= ' ') && (str[i] != '\0')) {
|
|
i++;
|
|
}
|
|
if ((err == 0) && (str[i] == '\0')) {
|
|
(*res) = val;
|
|
return PICO_OK;
|
|
} else {
|
|
(*res) = 0;
|
|
return PICO_EXC_NUMBER_FORMAT;
|
|
}
|
|
}
|
|
|
|
/* 'stringlen' is the part of input string to be considered,
|
|
* possibly not containing NULLC (e.g. result of strlen).
|
|
* 'maxsize' is the maximal size of 'part' including a byte
|
|
* for the terminating NULLC! */
|
|
void picoos_get_sep_part_str(picoos_char string[],
|
|
picoos_int32 stringlen, picoos_int32 * ind, picoos_char sepCh,
|
|
picoos_char part[], picoos_int32 maxsize, picoos_uint8 * done)
|
|
{
|
|
|
|
picoos_int32 j;
|
|
picoos_uint8 done1;
|
|
|
|
if (((*ind) >= stringlen)) {
|
|
(*done) = 0;
|
|
part[0] = (picoos_char) NULLC;
|
|
} else {
|
|
done1 = 1;
|
|
j = 0;
|
|
while ((((*ind) < stringlen) && (string[(*ind)] != sepCh)) && (string[((*ind))] != (picoos_char)NULLC)) {
|
|
if ((j < maxsize-1)) {
|
|
part[(j)] = string[(*ind)];
|
|
j++;
|
|
} else {
|
|
done1 = 0;
|
|
}
|
|
(*ind)++;
|
|
}
|
|
part[j] = (picoos_char)NULLC;
|
|
if ((*ind) < stringlen) {
|
|
if ((string[(*ind)] == sepCh)) {
|
|
(*ind)++; /* skip separator character */
|
|
} else if (string[(*ind)] == (picoos_char)NULLC) {
|
|
/* reached end of input; set ind to stringlen so that no
|
|
more (empty) partial strings will be found */
|
|
(*ind) = stringlen;
|
|
}
|
|
}
|
|
(*done) = done1;
|
|
}
|
|
}
|
|
|
|
/* *****************************************************************/
|
|
/* timer function */
|
|
/* *****************************************************************/
|
|
|
|
extern void picoos_get_timer(picopal_uint32 * sec, picopal_uint32 * usec)
|
|
{
|
|
picopal_get_timer(sec, usec);
|
|
}
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
|
|
/* end */
|