blob: ae28ede3f043faf81a062eefcce33d102d39f4a8 [file] [log] [blame]
/*
* 'ajduk' specific functionality, examples for low memory techniques
*/
#ifdef DUK_CMDLINE_AJSHEAP
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "ajs.h"
#include "ajs_heap.h"
#include "duktape.h"
extern uint8_t dbgHEAPDUMP;
#if defined(DUK_USE_ROM_OBJECTS) && defined(DUK_USE_HEAPPTR16)
/* Pointer compression with ROM strings/objects:
*
* For now, use DUK_USE_ROM_OBJECTS to signal the need for compressed ROM
* pointers. DUK_USE_ROM_PTRCOMP_FIRST is provided for the ROM pointer
* compression range minimum to avoid duplication in user code.
*/
#if 0 /* This extern declaration is provided by duktape.h, array provided by duktape.c. */
extern const void * const duk_rom_compressed_pointers[];
#endif
static const void *duk__romptr_low = NULL;
static const void *duk__romptr_high = NULL;
#define DUK__ROMPTR_COMPRESSION
#define DUK__ROMPTR_FIRST DUK_USE_ROM_PTRCOMP_FIRST
#endif
/*
* Helpers
*/
static void *ajduk__lose_const(const void *ptr) {
/* Somewhat portable way of losing a const without warnings.
* Another approach is to cast through intptr_t, but that
* type is not always available.
*/
union {
const void *p;
void *q;
} u;
u.p = ptr;
return u.q;
}
static void safe_print_chars(const char *p, duk_size_t len, int until_nul) {
duk_size_t i;
printf("\"");
for (i = 0; i < len; i++) {
unsigned char x = (unsigned char) p[i];
if (until_nul && x == 0U) {
break;
}
if (x < 0x20 || x >= 0x7e || x == '"' || x == '\'' || x == '\\') {
printf("\\x%02x", (int) x);
} else {
printf("%c", (char) x);
}
}
printf("\"");
}
/*
* Heap initialization when using AllJoyn.js pool allocator (without any
* other AllJoyn.js integration). This serves as an example of how to
* integrate Duktape with a pool allocator and is useful for low memory
* testing.
*
* The pool sizes are not optimized here. The sizes are chosen so that
* you can look at the high water mark (hwm) and use counts (use) and see
* how much allocations are needed for each pool size. To optimize pool
* sizes more accurately, you can use --alloc-logging and inspect the memory
* allocation log which provides exact byte counts etc.
*
* https://git.allseenalliance.org/cgit/core/alljoyn-js.git
* https://git.allseenalliance.org/cgit/core/alljoyn-js.git/tree/ajs.c
*/
static const AJS_HeapConfig ajsheap_config[] = {
{ 8, 10, AJS_POOL_BORROW, 0 },
{ 12, 600, AJS_POOL_BORROW, 0 },
{ 16, 300, AJS_POOL_BORROW, 0 },
{ 20, 300, AJS_POOL_BORROW, 0 },
{ 24, 300, AJS_POOL_BORROW, 0 },
{ 28, 150, AJS_POOL_BORROW, 0 },
{ 32, 150, AJS_POOL_BORROW, 0 },
{ 40, 150, AJS_POOL_BORROW, 0 },
{ 48, 50, AJS_POOL_BORROW, 0 },
{ 52, 50, AJS_POOL_BORROW, 0 },
{ 56, 50, AJS_POOL_BORROW, 0 },
{ 60, 50, AJS_POOL_BORROW, 0 },
{ 64, 50, AJS_POOL_BORROW, 0 },
{ 128, 80, AJS_POOL_BORROW, 0 },
{ 256, 16, AJS_POOL_BORROW, 0 },
{ 320, 1, AJS_POOL_BORROW, 0 },
{ 392, 1, AJS_POOL_BORROW, 0 }, /* duk_hthread, with heap ptr compression, ROM strings+objects */
{ 512, 16, AJS_POOL_BORROW, 0 },
{ 964, 1, AJS_POOL_BORROW, 0 }, /* duk_heap, with heap ptr compression, ROM strings+objects */
{ 1024, 6, AJS_POOL_BORROW, 0 },
{ 1344, 1, AJS_POOL_BORROW, 0 }, /* duk_heap, with heap ptr compression, RAM strings+objects */
{ 2048, 5, AJS_POOL_BORROW, 0 },
{ 4096, 3, 0, 0 },
{ 8192, 3, 0, 0 },
{ 16384, 1, 0, 0 },
{ 32768, 1, 0, 0 }
};
uint8_t *ajsheap_ram = NULL;
void ajsheap_init(void) {
size_t heap_sz[1];
uint8_t *heap_array[1];
uint8_t num_pools, i;
AJ_Status ret;
num_pools = (uint8_t) (sizeof(ajsheap_config) / sizeof(AJS_HeapConfig));
heap_sz[0] = AJS_HeapRequired(ajsheap_config, /* heapConfig */
num_pools, /* numPools */
0); /* heapNum */
ajsheap_ram = (uint8_t *) malloc(heap_sz[0]);
if (ajsheap_ram == NULL) {
fprintf(stderr, "Failed to allocate AJS heap\n");
fflush(stderr);
exit(1);
}
heap_array[0] = ajsheap_ram;
fprintf(stderr, "Allocated AJS heap of %ld bytes, pools:", (long) heap_sz[0]);
for (i = 0; i < num_pools; i++) {
fprintf(stderr, " (sz:%ld,num:%ld,brw:%ld,idx:%ld)",
(long) ajsheap_config[i].size, (long) ajsheap_config[i].entries,
(long) ajsheap_config[i].borrow, (long) ajsheap_config[i].heapIndex);
}
fprintf(stderr, "\n");
fflush(stderr);
ret = AJS_HeapInit((void **) heap_array, /* heap */
(size_t *) heap_sz, /* heapSz */
ajsheap_config, /* heapConfig */
num_pools, /* numPools */
1); /* numHeaps */
fprintf(stderr, "AJS_HeapInit() -> %ld\n", (long) ret);
fflush(stderr);
/* Enable heap dumps */
dbgHEAPDUMP = 1;
#if defined(DUK__ROMPTR_COMPRESSION)
/* Scan ROM pointer range for faster detection of "is 'p' a ROM pointer"
* later on.
*/
if (1) {
const void * const * ptrs = (const void * const *) duk_rom_compressed_pointers;
duk__romptr_low = duk__romptr_high = (const void *) *ptrs;
while (*ptrs) {
if (*ptrs > duk__romptr_high) {
duk__romptr_high = (const void *) *ptrs;
}
if (*ptrs < duk__romptr_low) {
duk__romptr_low = (const void *) *ptrs;
}
ptrs++;
}
fprintf(stderr, "romptrs: low=%p high=%p\n",
(const void *) duk__romptr_low, (const void *) duk__romptr_high);
fflush(stderr);
}
#endif
}
void ajsheap_free(void) {
if (ajsheap_ram != NULL) {
free(ajsheap_ram);
ajsheap_ram = NULL;
}
}
/* AjsHeap.dump(), allows Ecmascript code to dump heap status at suitable
* points.
*/
duk_ret_t ajsheap_dump_binding(duk_context *ctx) {
AJS_HeapDump();
fflush(stdout);
return 0;
}
void ajsheap_dump(void) {
AJS_HeapDump();
fflush(stdout);
}
void ajsheap_register(duk_context *ctx) {
duk_push_object(ctx);
duk_push_c_function(ctx, ajsheap_dump_binding, 0);
duk_put_prop_string(ctx, -2, "dump");
duk_put_global_string(ctx, "AjsHeap");
}
/*
* Wrapped ajs_heap.c alloc functions
*
* Used to write an alloc log.
*/
static FILE *ajsheap_alloc_log = NULL;
static void ajsheap_write_alloc_log(const char *fmt, ...) {
va_list ap;
char buf[256];
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
buf[sizeof(buf) - 1] = (char) 0;
va_end(ap);
if (ajsheap_alloc_log == NULL) {
ajsheap_alloc_log = fopen("/tmp/ajduk-alloc-log.txt", "wb");
if (ajsheap_alloc_log == NULL) {
fprintf(stderr, "WARNING: failed to write alloc log, ignoring\n");
fflush(stderr);
return;
}
}
(void) fwrite((const void *) buf, 1, strlen(buf), ajsheap_alloc_log);
(void) fflush(ajsheap_alloc_log);
}
void *ajsheap_alloc_wrapped(void *udata, duk_size_t size) {
void *ret = AJS_Alloc(udata, size);
if (size > 0 && ret == NULL) {
ajsheap_write_alloc_log("A FAIL %ld\n", (long) size);
} else if (ret == NULL) {
ajsheap_write_alloc_log("A NULL %ld\n", (long) size);
} else {
ajsheap_write_alloc_log("A %p %ld\n", ret, (long) size);
}
return ret;
}
void *ajsheap_realloc_wrapped(void *udata, void *ptr, duk_size_t size) {
void *ret = AJS_Realloc(udata, ptr, size);
if (size > 0 && ret == NULL) {
if (ptr == NULL) {
ajsheap_write_alloc_log("R NULL -1 FAIL %ld\n", (long) size);
} else {
ajsheap_write_alloc_log("R %p -1 FAIL %ld\n", ptr, (long) size);
}
} else if (ret == NULL) {
if (ptr == NULL) {
ajsheap_write_alloc_log("R NULL -1 NULL %ld\n", (long) size);
} else {
ajsheap_write_alloc_log("R %p -1 NULL %ld\n", ptr, (long) size);
}
} else {
if (ptr == NULL) {
ajsheap_write_alloc_log("R NULL -1 %p %ld\n", ret, (long) size);
} else {
ajsheap_write_alloc_log("R %p -1 %p %ld\n", ptr, ret, (long) size);
}
}
return ret;
}
void ajsheap_free_wrapped(void *udata, void *ptr) {
AJS_Free(udata, ptr);
if (ptr == NULL) {
} else {
ajsheap_write_alloc_log("F %p -1\n", ptr);
}
}
/*
* Example pointer compression functions.
*
* 'base' is chosen so that no non-NULL pointer results in a zero result
* which is reserved for NULL pointers.
*/
duk_uint16_t ajsheap_enc16(void *ud, void *p) {
duk_uint32_t ret;
char *base = (char *) ajsheap_ram - 4;
#if defined(DUK__ROMPTR_COMPRESSION)
if (p >= duk__romptr_low && p <= duk__romptr_high) {
/* The if-condition should be the fastest possible check
* for "is 'p' in ROM?". If pointer is in ROM, we'd like
* to compress it quickly. Here we just scan a ~1K array
* which is very bad for performance and for illustration
* only.
*/
const void * const * ptrs = duk_rom_compressed_pointers;
while (*ptrs) {
if (*ptrs == p) {
ret = DUK__ROMPTR_FIRST + (ptrs - duk_rom_compressed_pointers);
#if 0
fprintf(stderr, "ajsheap_enc16: rom pointer: %p -> 0x%04lx\n", (void *) p, (long) ret);
fflush(stderr);
#endif
return (duk_uint16_t) ret;
}
ptrs++;
}
/* We should really never be here: Duktape should only be
* compressing pointers which are in the ROM compressed
* pointers list, which are known at 'make dist' time.
* We go on, causing a pointer compression error.
*/
fprintf(stderr, "ajsheap_enc16: rom pointer: %p could not be compressed, should never happen\n", (void *) p);
fflush(stderr);
}
#endif
/* Userdata is not needed in this case but would be useful if heap
* pointer compression were used for multiple heaps. The userdata
* allows the callback to distinguish between heaps and their base
* pointers.
*
* If not needed, the userdata can be left out during compilation
* by simply ignoring the userdata argument of the pointer encode
* and decode macros. It is kept here so that any bugs in actually
* providing the value inside Duktape are revealed during compilation.
*/
(void) ud;
#if 1
/* Ensure that we always get the heap_udata given in heap creation.
* (Useful for Duktape development, not needed for user programs.)
*/
if (ud != (void *) 0xdeadbeef) {
fprintf(stderr, "invalid udata for ajsheap_enc16: %p\n", ud);
fflush(stderr);
}
#endif
if (p == NULL) {
ret = 0;
} else {
ret = (duk_uint32_t) (((char *) p - base) >> 2);
}
#if 0
printf("ajsheap_enc16: %p -> %u\n", p, (unsigned int) ret);
#endif
if (ret > 0xffffUL) {
fprintf(stderr, "Failed to compress pointer: %p (ret was %ld)\n", (void *) p, (long) ret);
fflush(stderr);
abort();
}
#if defined(DUK__ROMPTR_COMPRESSION)
if (ret >= DUK__ROMPTR_FIRST) {
fprintf(stderr, "Failed to compress pointer, in 16-bit range but matches romptr range: %p (ret was %ld)\n", (void *) p, (long) ret);
fflush(stderr);
abort();
}
#endif
return (duk_uint16_t) ret;
}
void *ajsheap_dec16(void *ud, duk_uint16_t x) {
void *ret;
char *base = (char *) ajsheap_ram - 4;
#if defined(DUK__ROMPTR_COMPRESSION)
if (x >= DUK__ROMPTR_FIRST) {
/* This is a blind lookup, could check index validity.
* Duktape should never decompress a pointer which would
* be out-of-bounds here.
*/
ret = (void *) ajduk__lose_const(duk_rom_compressed_pointers[x - DUK__ROMPTR_FIRST]);
#if 0
fprintf(stderr, "ajsheap_dec16: rom pointer: 0x%04lx -> %p\n", (long) x, ret);
fflush(stderr);
#endif
return ret;
}
#endif
/* See userdata discussion in ajsheap_enc16(). */
(void) ud;
#if 1
/* Ensure that we always get the heap_udata given in heap creation. */
if (ud != (void *) 0xdeadbeef) {
fprintf(stderr, "invalid udata for ajsheap_dec16: %p\n", ud);
fflush(stderr);
}
#endif
if (x == 0) {
ret = NULL;
} else {
ret = (void *) (base + (((duk_uint32_t) x) << 2));
}
#if 0
printf("ajsheap_dec16: %u -> %p\n", (unsigned int) x, ret);
#endif
return ret;
}
/*
* Simplified example of an external strings strategy where incoming strings
* are written sequentially into a fixed, memory mapped flash area.
*
* The example first scans if the string is already in the flash (which may
* happen if the same string is interned multiple times), then adds it to
* flash if there is space.
*
* This example is too slow to be used in a real world application: there
* should be e.g. a hash table to quickly check for strings that are already
* present in the string data (similarly to how string interning works in
* Duktape itself).
*/
static uint8_t ajsheap_strdata[65536];
static size_t ajsheap_strdata_used = 0;
const void *ajsheap_extstr_check_1(const void *ptr, duk_size_t len) {
uint8_t *p, *p_end;
uint8_t initial;
uint8_t *ret;
size_t left;
(void) safe_print_chars; /* potentially unused */
if (len <= 3) {
/* It's not worth it to make very small strings external, as
* they would take the same space anyway. Also avoids zero
* length degenerate case.
*/
return NULL;
}
/*
* Check if we already have the string. Be careful to compare for
* NUL terminator too, it is NOT present in 'ptr'. This algorithm
* is too simplistic and way too slow for actual use.
*/
initial = ((const uint8_t *) ptr)[0];
for (p = ajsheap_strdata, p_end = p + ajsheap_strdata_used; p != p_end; p++) {
if (*p != initial) {
continue;
}
left = (size_t) (p_end - p);
if (left >= len + 1 &&
memcmp(p, ptr, len) == 0 &&
p[len] == 0) {
ret = p;
#if 0
printf("ajsheap_extstr_check_1: ptr=%p, len=%ld ",
(void *) ptr, (long) len);
safe_print_chars((const char *) ptr, len, 0 /*until_nul*/);
printf(" -> existing %p (used=%ld)\n",
(void *) ret, (long) ajsheap_strdata_used);
#endif
return ret;
}
}
/*
* Not present yet, check if we have space. Again, be careful to
* ensure there is space for a NUL following the input data.
*/
if (ajsheap_strdata_used + len + 1 > sizeof(ajsheap_strdata)) {
#if 0
printf("ajsheap_extstr_check_1: ptr=%p, len=%ld ", (void *) ptr, (long) len);
safe_print_chars((const char *) ptr, len, 0 /*until_nul*/);
printf(" -> no space (used=%ld)\n", (long) ajsheap_strdata_used);
#endif
return NULL;
}
/*
* There is space, add the string to our collection, being careful
* to append the NUL.
*/
ret = ajsheap_strdata + ajsheap_strdata_used;
memcpy(ret, ptr, len);
ret[len] = (uint8_t) 0;
ajsheap_strdata_used += len + 1;
#if 0
printf("ajsheap_extstr_check_1: ptr=%p, len=%ld -> ", (void *) ptr, (long) len);
safe_print_chars((const char *) ptr, len, 0 /*until_nul*/);
printf(" -> %p (used=%ld)\n", (void *) ret, (long) ajsheap_strdata_used);
#endif
return (const void *) ret;
}
void ajsheap_extstr_free_1(const void *ptr) {
(void) ptr;
#if 0
printf("ajsheap_extstr_free_1: freeing extstr %p -> ", ptr);
safe_print_chars((const char *) ptr, DUK_SIZE_MAX, 1 /*until_nul*/);
printf("\n");
#endif
}
/*
* Simplified example of an external strings strategy where a set of strings
* is gathered during application compile time and baked into the application
* binary.
*
* Duktape built-in strings are available from duk_build_meta.json, see
* util/duk_meta_to_strarray.py. There may also be a lot of application
* specific strings, e.g. those used by application specific APIs. These
* must be gathered through some other means, see e.g. util/scan_strings.py.
*/
static const char *strdata_duk_builtin_strings[] = {
/*
* These strings are from util/duk_meta_to_strarray.py
*/
"Logger",
"Thread",
"Pointer",
"Buffer",
"DecEnv",
"ObjEnv",
"",
"global",
"Arguments",
"JSON",
"Math",
"Error",
"RegExp",
"Date",
"Number",
"Boolean",
"String",
"Array",
"Function",
"Object",
"Null",
"Undefined",
"{_func:true}",
"{\x22" "_func\x22" ":true}",
"{\x22" "_ninf\x22" ":true}",
"{\x22" "_inf\x22" ":true}",
"{\x22" "_nan\x22" ":true}",
"{\x22" "_undef\x22" ":true}",
"toLogString",
"clog",
"l",
"n",
"fatal",
"error",
"warn",
"debug",
"trace",
"raw",
"fmt",
"current",
"resume",
"compact",
"jc",
"jx",
"base64",
"hex",
"dec",
"enc",
"fin",
"gc",
"act",
"info",
"version",
"env",
"modLoaded",
"modSearch",
"errThrow",
"errCreate",
"compile",
"\xff" "Regbase",
"\xff" "Thread",
"\xff" "Handler",
"\xff" "Finalizer",
"\xff" "Callee",
"\xff" "Map",
"\xff" "Args",
"\xff" "This",
"\xff" "Pc2line",
"\xff" "Source",
"\xff" "Varenv",
"\xff" "Lexenv",
"\xff" "Varmap",
"\xff" "Formals",
"\xff" "Bytecode",
"\xff" "Next",
"\xff" "Target",
"\xff" "Value",
"pointer",
"buffer",
"\xff" "Tracedata",
"lineNumber",
"fileName",
"pc",
"stack",
"ThrowTypeError",
"Duktape",
"id",
"require",
"__proto__",
"setPrototypeOf",
"ownKeys",
"enumerate",
"deleteProperty",
"has",
"Proxy",
"callee",
"Invalid Date",
"[...]",
"\x0a" "\x09",
" ",
",",
"-0",
"+0",
"0",
"-Infinity",
"+Infinity",
"Infinity",
"object",
"string",
"number",
"boolean",
"undefined",
"stringify",
"tan",
"sqrt",
"sin",
"round",
"random",
"pow",
"min",
"max",
"log",
"floor",
"exp",
"cos",
"ceil",
"atan2",
"atan",
"asin",
"acos",
"abs",
"SQRT2",
"SQRT1_2",
"PI",
"LOG10E",
"LOG2E",
"LN2",
"LN10",
"E",
"message",
"name",
"input",
"index",
"(?:)",
"lastIndex",
"multiline",
"ignoreCase",
"source",
"test",
"exec",
"toGMTString",
"setYear",
"getYear",
"toJSON",
"toISOString",
"toUTCString",
"setUTCFullYear",
"setFullYear",
"setUTCMonth",
"setMonth",
"setUTCDate",
"setDate",
"setUTCHours",
"setHours",
"setUTCMinutes",
"setMinutes",
"setUTCSeconds",
"setSeconds",
"setUTCMilliseconds",
"setMilliseconds",
"setTime",
"getTimezoneOffset",
"getUTCMilliseconds",
"getMilliseconds",
"getUTCSeconds",
"getSeconds",
"getUTCMinutes",
"getMinutes",
"getUTCHours",
"getHours",
"getUTCDay",
"getDay",
"getUTCDate",
"getDate",
"getUTCMonth",
"getMonth",
"getUTCFullYear",
"getFullYear",
"getTime",
"toLocaleTimeString",
"toLocaleDateString",
"toTimeString",
"toDateString",
"now",
"UTC",
"parse",
"toPrecision",
"toExponential",
"toFixed",
"POSITIVE_INFINITY",
"NEGATIVE_INFINITY",
"NaN",
"MIN_VALUE",
"MAX_VALUE",
"substr",
"trim",
"toLocaleUpperCase",
"toUpperCase",
"toLocaleLowerCase",
"toLowerCase",
"substring",
"split",
"search",
"replace",
"match",
"localeCompare",
"charCodeAt",
"charAt",
"fromCharCode",
"reduceRight",
"reduce",
"filter",
"map",
"forEach",
"some",
"every",
"lastIndexOf",
"indexOf",
"unshift",
"splice",
"sort",
"slice",
"shift",
"reverse",
"push",
"pop",
"join",
"concat",
"isArray",
"arguments",
"caller",
"bind",
"call",
"apply",
"propertyIsEnumerable",
"isPrototypeOf",
"hasOwnProperty",
"valueOf",
"toLocaleString",
"toString",
"constructor",
"set",
"get",
"enumerable",
"configurable",
"writable",
"value",
"keys",
"isExtensible",
"isFrozen",
"isSealed",
"preventExtensions",
"freeze",
"seal",
"defineProperties",
"defineProperty",
"create",
"getOwnPropertyNames",
"getOwnPropertyDescriptor",
"getPrototypeOf",
"prototype",
"length",
"alert",
"print",
"unescape",
"escape",
"encodeURIComponent",
"encodeURI",
"decodeURIComponent",
"decodeURI",
"isFinite",
"isNaN",
"parseFloat",
"parseInt",
"eval",
"URIError",
"TypeError",
"SyntaxError",
"ReferenceError",
"RangeError",
"EvalError",
"break",
"case",
"catch",
"continue",
"debugger",
"default",
"delete",
"do",
"else",
"finally",
"for",
"function",
"if",
"in",
"instanceof",
"new",
"return",
"switch",
"this",
"throw",
"try",
"typeof",
"var",
"void",
"while",
"with",
"class",
"const",
"enum",
"export",
"extends",
"import",
"super",
"null",
"true",
"false",
"implements",
"interface",
"let",
"package",
"private",
"protected",
"public",
"static",
"yield",
/*
* These strings are manually added, and would be gathered in some
* application specific manner.
*/
"foo",
"bar",
"quux",
"enableFrob",
"disableFrob"
/* ... */
};
const void *ajsheap_extstr_check_2(const void *ptr, duk_size_t len) {
int i, n;
(void) safe_print_chars; /* potentially unused */
/* Linear scan. An actual implementation would need some acceleration
* structure, e.g. select a sublist based on first character.
*
* NOTE: input string (behind 'ptr' with 'len' bytes) DOES NOT have a
* trailing NUL character. Any strings returned from this function
* MUST have a trailing NUL character.
*/
n = (int) (sizeof(strdata_duk_builtin_strings) / sizeof(const char *));
for (i = 0; i < n; i++) {
const char *str;
str = strdata_duk_builtin_strings[i];
if (strlen(str) == len && memcmp(ptr, (const void *) str, len) == 0) {
#if 0
printf("ajsheap_extstr_check_2: ptr=%p, len=%ld ",
(void *) ptr, (long) len);
safe_print_chars((const char *) ptr, len, 0 /*until_nul*/);
printf(" -> constant string index %ld\n", (long) i);
#endif
return (void *) ajduk__lose_const(strdata_duk_builtin_strings[i]);
}
}
#if 0
printf("ajsheap_extstr_check_2: ptr=%p, len=%ld ",
(void *) ptr, (long) len);
safe_print_chars((const char *) ptr, len, 0 /*until_nul*/);
printf(" -> not found\n");
#endif
return NULL;
}
void ajsheap_extstr_free_2(const void *ptr) {
(void) ptr;
#if 0
printf("ajsheap_extstr_free_2: freeing extstr %p -> ", ptr);
safe_print_chars((const char *) ptr, DUK_SIZE_MAX, 1 /*until_nul*/);
printf("\n");
#endif
}
/*
* External strings strategy intended for valgrind testing: external strings
* are allocated using malloc()/free() so that valgrind can be used to ensure
* that strings are e.g. freed exactly once.
*/
const void *ajsheap_extstr_check_3(const void *ptr, duk_size_t len) {
duk_uint8_t *ret;
(void) safe_print_chars; /* potentially unused */
ret = malloc((size_t) len + 1);
if (ret == NULL) {
#if 0
printf("ajsheap_extstr_check_3: ptr=%p, len=%ld ",
(void *) ptr, (long) len);
safe_print_chars((const char *) ptr, len, 0 /*until_nul*/);
printf(" -> malloc failed, return NULL\n");
#endif
return (const void *) NULL;
}
if (len > 0) {
memcpy((void *) ret, ptr, (size_t) len);
}
ret[len] = (duk_uint8_t) 0;
#if 0
printf("ajsheap_extstr_check_3: ptr=%p, len=%ld ",
(void *) ptr, (long) len);
safe_print_chars((const char *) ptr, len, 0 /*until_nul*/);
printf(" -> %p\n", (void *) ret);
#endif
return (const void *) ret;
}
void ajsheap_extstr_free_3(const void *ptr) {
(void) ptr;
#if 0
printf("ajsheap_extstr_free_3: freeing extstr %p -> ", ptr);
safe_print_chars((const char *) ptr, DUK_SIZE_MAX, 1 /*until_nul*/);
printf("\n");
#endif
free((void *) ajduk__lose_const(ptr));
}
/*
* Execution timeout example
*/
#define AJSHEAP_EXEC_TIMEOUT 5 /* seconds */
static time_t curr_pcall_start = 0;
static long exec_timeout_check_counter = 0;
void ajsheap_start_exec_timeout(void) {
curr_pcall_start = time(NULL);
}
void ajsheap_clear_exec_timeout(void) {
curr_pcall_start = 0;
}
duk_bool_t ajsheap_exec_timeout_check(void *udata) {
time_t now = time(NULL);
time_t diff = now - curr_pcall_start;
(void) udata; /* not needed */
exec_timeout_check_counter++;
#if 0
printf("exec timeout check %ld: now=%ld, start=%ld, diff=%ld\n",
(long) exec_timeout_check_counter, (long) now, (long) curr_pcall_start, (long) diff);
fflush(stdout);
#endif
if (curr_pcall_start == 0) {
/* protected call not yet running */
return 0;
}
if (diff > AJSHEAP_EXEC_TIMEOUT) {
return 1;
}
return 0;
}
#else /* DUK_CMDLINE_AJSHEAP */
int ajs_dummy = 0; /* to avoid empty source file */
#endif /* DUK_CMDLINE_AJSHEAP */