LP-89 - Port OP_15.05.01 fixes. Release notes:
[librepilot.git] / flight / libraries / PyMite / vm / strobj.c
blobed89d145527cf2b97e6ae093130a2aa3593dc1c0
1 /*
2 # This file is Copyright 2003, 2006, 2007, 2009, 2010 Dean Hall.
4 # This file is part of the PyMite VM.
5 # The PyMite VM is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU GENERAL PUBLIC LICENSE Version 2.
8 # The PyMite VM is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 # A copy of the GNU GENERAL PUBLIC LICENSE Version 2
12 # is seen in the file COPYING in this directory.
16 #undef __FILE_ID__
17 #define __FILE_ID__ 0x12
20 /**
21 * \file
22 * \brief String Object Type
24 * String object type opeartions.
27 #include "pm.h"
30 #if USE_STRING_CACHE
31 /** String obj cachche: a list of all string objects. */
32 static pPmString_t pstrcache = C_NULL;
33 #endif /* USE_STRING_CACHE */
37 * If USE_STRING_CACHE is defined nonzero, the string cache
38 * will be searched for an existing String object.
39 * If not found, a new object is created and inserted
40 * into the cache.
42 PmReturn_t
43 string_create(PmMemSpace_t memspace, uint8_t const **paddr, int16_t len,
44 int16_t n, pPmObj_t *r_pstring)
46 PmReturn_t retval = PM_RET_OK;
47 pPmString_t pstr = C_NULL;
48 uint8_t *pdst = C_NULL;
49 uint8_t const *psrc = C_NULL;
51 #if USE_STRING_CACHE
52 pPmString_t pcacheentry = C_NULL;
53 #endif /* USE_STRING_CACHE */
54 uint8_t *pchunk;
56 /* If loading from an image, get length from the image */
57 if (len < 0)
59 len = mem_getWord(memspace, paddr);
62 /* If loading from a C string, get its strlen (first null) */
63 else if (len == 0)
65 len = sli_strlen((char const *)*paddr);
68 /* Get space for String obj */
69 retval = heap_getChunk(sizeof(PmString_t) + len * n, &pchunk);
70 PM_RETURN_IF_ERROR(retval);
71 pstr = (pPmString_t)pchunk;
73 /* Fill the string obj */
74 OBJ_SET_TYPE(pstr, OBJ_TYPE_STR);
75 pstr->length = len * n;
77 /* Copy C-string into String obj */
78 pdst = (uint8_t *)&(pstr->val);
79 while (--n >= 0)
81 psrc = *paddr;
82 mem_copy(memspace, &pdst, &psrc, len);
85 /* Be sure paddr points to one byte past the end of the source string */
86 *paddr = psrc;
88 /* Zero-pad end of string */
89 for (; pdst < (uint8_t *)pstr + OBJ_GET_SIZE(pstr); pdst++)
91 *pdst = 0;
94 #if USE_STRING_CACHE
95 /* Check for twin string in cache */
96 for (pcacheentry = pstrcache;
97 pcacheentry != C_NULL; pcacheentry = pcacheentry->next)
99 /* If string already exists */
100 if (string_compare(pcacheentry, pstr) == C_SAME)
102 /* Free the string */
103 retval = heap_freeChunk((pPmObj_t)pstr);
105 /* Return ptr to old */
106 *r_pstring = (pPmObj_t)pcacheentry;
107 return retval;
111 /* Insert string obj into cache */
112 pstr->next = pstrcache;
113 pstrcache = pstr;
115 #endif /* USE_STRING_CACHE */
117 *r_pstring = (pPmObj_t)pstr;
118 return PM_RET_OK;
122 PmReturn_t
123 string_newFromChar(uint8_t const c, pPmObj_t *r_pstring)
125 PmReturn_t retval;
126 uint8_t cstr[2];
127 uint8_t const *pcstr;
129 cstr[0] = c;
130 cstr[1] = '\0';
131 pcstr = cstr;
133 retval = string_new(&pcstr, r_pstring);
135 /* If c was a null character, force the length to 1 */
136 if (c == '\0')
138 ((pPmString_t)*r_pstring)->length = 1;
141 return retval;
145 int8_t
146 string_compare(pPmString_t pstr1, pPmString_t pstr2)
148 /* Return false if lengths are not equal */
149 if (pstr1->length != pstr2->length)
151 return C_DIFFER;
154 /* Compare the strings' contents */
155 return sli_strncmp((char const *)&(pstr1->val),
156 (char const *)&(pstr2->val),
157 pstr1->length) == 0 ? C_SAME : C_DIFFER;
161 #ifdef HAVE_PRINT
162 PmReturn_t
163 string_printFormattedBytes(uint8_t *pb, uint8_t is_escaped, uint16_t n)
165 uint16_t i;
166 uint8_t ch;
167 uint8_t nibble;
168 PmReturn_t retval = PM_RET_OK;
170 if (is_escaped)
172 retval = plat_putByte('\'');
173 PM_RETURN_IF_ERROR(retval);
176 for (i = 0; i < n; i++)
178 ch = pb[i];
179 if (is_escaped && (ch == '\\'))
181 /* Output an additional backslash to escape it. */
182 retval = plat_putByte('\\');
183 PM_RETURN_IF_ERROR(retval);
186 /* Print the hex escape code of non-printable characters */
187 if (is_escaped
188 && ((ch < (uint8_t)32) || (ch >= (uint8_t)128) || (ch == '\'')))
190 plat_putByte('\\');
191 plat_putByte('x');
193 nibble = (ch >> (uint8_t)4) + '0';
194 if (nibble > '9')
195 nibble += ('a' - '0' - (uint8_t)10);
196 plat_putByte(nibble);
198 nibble = (ch & (uint8_t)0x0F) + '0';
199 if (nibble > '9')
200 nibble += ('a' - '0' - (uint8_t)10);
201 plat_putByte(nibble);
203 else
205 /* Simply output character */
206 retval = plat_putByte(ch);
207 PM_RETURN_IF_ERROR(retval);
210 if (is_escaped)
212 retval = plat_putByte('\'');
215 return retval;
219 PmReturn_t
220 string_print(pPmObj_t pstr, uint8_t is_escaped)
222 PmReturn_t retval = PM_RET_OK;
224 C_ASSERT(pstr != C_NULL);
226 /* Ensure string obj */
227 if (OBJ_GET_TYPE(pstr) != OBJ_TYPE_STR)
229 PM_RAISE(retval, PM_RET_EX_TYPE);
230 return retval;
233 retval = string_printFormattedBytes(&(((pPmString_t)pstr)->val[0]),
234 is_escaped,
235 ((pPmString_t)pstr)->length);
237 return retval;
239 #endif /* HAVE_PRINT */
242 PmReturn_t
243 string_cacheInit(void)
245 #if USE_STRING_CACHE
246 pstrcache = C_NULL;
247 #endif
248 return PM_RET_OK;
252 PmReturn_t
253 string_getCache(pPmString_t **r_ppstrcache)
255 #if USE_STRING_CACHE
256 *r_ppstrcache = &pstrcache;
257 #else
258 *r_ppstrcache = C_NULL;
259 #endif
260 return PM_RET_OK;
264 PmReturn_t
265 string_concat(pPmString_t pstr1, pPmString_t pstr2, pPmObj_t *r_pstring)
267 PmReturn_t retval = PM_RET_OK;
268 pPmString_t pstr = C_NULL;
269 uint8_t *pdst = C_NULL;
270 uint8_t const *psrc = C_NULL;
271 #if USE_STRING_CACHE
272 pPmString_t pcacheentry = C_NULL;
273 #endif /* USE_STRING_CACHE */
274 uint8_t *pchunk;
275 uint16_t len;
277 /* Create the String obj */
278 len = pstr1->length + pstr2->length;
279 retval = heap_getChunk(sizeof(PmString_t) + len, &pchunk);
280 PM_RETURN_IF_ERROR(retval);
281 pstr = (pPmString_t)pchunk;
282 OBJ_SET_TYPE(pstr, OBJ_TYPE_STR);
283 pstr->length = len;
285 /* Concatenate C-strings into String obj and apply null terminator */
286 pdst = (uint8_t *)&(pstr->val);
287 psrc = (uint8_t const *)&(pstr1->val);
288 mem_copy(MEMSPACE_RAM, &pdst, &psrc, pstr1->length);
289 psrc = (uint8_t const *)&(pstr2->val);
290 mem_copy(MEMSPACE_RAM, &pdst, &psrc, pstr2->length);
291 *pdst = '\0';
293 #if USE_STRING_CACHE
294 /* Check for twin string in cache */
295 for (pcacheentry = pstrcache;
296 pcacheentry != C_NULL; pcacheentry = pcacheentry->next)
298 /* If string already exists */
299 if (string_compare(pcacheentry, pstr) == C_SAME)
301 /* Free the string */
302 retval = heap_freeChunk((pPmObj_t)pstr);
304 /* Return ptr to old */
305 *r_pstring = (pPmObj_t)pcacheentry;
306 return retval;
310 /* Insert string obj into cache */
311 pstr->next = pstrcache;
312 pstrcache = pstr;
313 #endif /* USE_STRING_CACHE */
315 *r_pstring = (pPmObj_t)pstr;
316 return PM_RET_OK;
320 #ifdef HAVE_STRING_FORMAT
322 #define SIZEOF_FMTDBUF 42
323 #define SIZEOF_SMALLFMT 8
325 PmReturn_t
326 string_format(pPmString_t pstr, pPmObj_t parg, pPmObj_t *r_pstring)
328 PmReturn_t retval;
329 uint16_t strsize = 0;
330 uint16_t strindex;
331 uint8_t *fmtcstr;
332 uint8_t smallfmtcstr[SIZEOF_SMALLFMT];
333 uint8_t fmtdbuf[SIZEOF_FMTDBUF];
334 uint8_t i;
335 uint8_t j;
336 uint8_t argtupleindex = 0;
337 pPmObj_t pobj;
338 int snprintretval;
339 uint8_t expectedargcount = 0;
340 pPmString_t pnewstr;
341 uint8_t *pchunk;
342 #if USE_STRING_CACHE
343 pPmString_t pcacheentry = C_NULL;
344 #endif /* USE_STRING_CACHE */
346 /* Get the first arg */
347 pobj = parg;
349 /* Calculate the size of the resulting string */
350 fmtcstr = pstr->val;
351 for (i = 0; i < pstr->length; i++)
353 /* Count non-format chars */
354 if (fmtcstr[i] != '%') { strsize++; continue; }
356 /* If double percents, count one percent */
357 if (fmtcstr[++i] == '%') { strsize++; continue; }
359 /* Get arg from the tuple */
360 if (OBJ_GET_TYPE(parg) == OBJ_TYPE_TUP)
362 pobj = ((pPmTuple_t)parg)->val[argtupleindex++];
365 snprintretval = -1;
367 /* Format one arg to get its length */
368 smallfmtcstr[0] = '%';
369 for(j = 1; (i < pstr->length) && (j < SIZEOF_SMALLFMT); i++)
371 smallfmtcstr[j] = fmtcstr[i];
372 j++;
374 if ((fmtcstr[i] == 'd')
375 || (fmtcstr[i] == 'x')
376 || (fmtcstr[i] == 'X'))
378 if (OBJ_GET_TYPE(pobj) != OBJ_TYPE_INT)
380 PM_RAISE(retval, PM_RET_EX_TYPE);
381 return retval;
383 smallfmtcstr[j] = '\0';
384 snprintretval = snprintf((char *)fmtdbuf, SIZEOF_FMTDBUF,
385 (char *)smallfmtcstr, ((pPmInt_t)pobj)->val);
386 break;
389 #ifdef HAVE_FLOAT
390 else if (fmtcstr[i] == 'f')
392 if (OBJ_GET_TYPE(pobj) != OBJ_TYPE_FLT)
394 PM_RAISE(retval, PM_RET_EX_TYPE);
395 return retval;
397 smallfmtcstr[j] = '\0';
398 snprintretval = snprintf((char *)fmtdbuf, SIZEOF_FMTDBUF,
399 (char *)smallfmtcstr, ((pPmFloat_t)pobj)->val);
400 break;
402 #endif /* HAVE_FLOAT */
404 else if (fmtcstr[i] == 's')
406 if (OBJ_GET_TYPE(pobj) != OBJ_TYPE_STR)
408 PM_RAISE(retval, PM_RET_EX_TYPE);
409 return retval;
411 smallfmtcstr[j] = '\0';
412 snprintretval = snprintf((char *)fmtdbuf, SIZEOF_FMTDBUF,
413 (char *)smallfmtcstr, ((pPmString_t)pobj)->val);
414 break;
418 /* Raise ValueError if the format string was bad */
419 if (snprintretval < 0)
421 PM_RAISE(retval, PM_RET_EX_VAL);
422 return retval;
425 expectedargcount++;
426 strsize += snprintretval;
429 /* TypeError wrong number args */
430 if (((OBJ_GET_TYPE(parg) != OBJ_TYPE_TUP) && (expectedargcount != 1))
431 || ((OBJ_GET_TYPE(parg) == OBJ_TYPE_TUP)
432 && (expectedargcount != ((pPmTuple_t)parg)->length)))
434 PM_RAISE(retval, PM_RET_EX_TYPE);
435 return retval;
438 /* Allocate and initialize String obj */
439 retval = heap_getChunk(sizeof(PmString_t) + strsize, &pchunk);
440 PM_RETURN_IF_ERROR(retval);
441 pnewstr = (pPmString_t)pchunk;
442 OBJ_SET_TYPE(pnewstr, OBJ_TYPE_STR);
443 pnewstr->length = strsize;
446 /* Fill contents of String obj */
447 strindex = 0;
448 argtupleindex = 0;
449 pobj = parg;
451 for (i = 0; i < pstr->length; i++)
453 /* Copy non-format chars */
454 if (fmtcstr[i] != '%')
456 pnewstr->val[strindex++] = fmtcstr[i];
457 continue;
460 /* If double percents, copy one percent */
461 if (fmtcstr[++i] == '%')
463 pnewstr->val[strindex++] = '%';
464 continue;
467 /* Get arg from the tuple */
468 if (OBJ_GET_TYPE(parg) == OBJ_TYPE_TUP)
470 pobj = ((pPmTuple_t)parg)->val[argtupleindex++];
473 snprintretval = -1;
475 /* Format one arg to get its length */
476 smallfmtcstr[0] = '%';
477 for(j = 1; (i < pstr->length) && (j < SIZEOF_SMALLFMT); i++)
479 smallfmtcstr[j] = fmtcstr[i];
480 j++;
482 if ((fmtcstr[i] == 'd')
483 || (fmtcstr[i] == 'x')
484 || (fmtcstr[i] == 'X'))
486 smallfmtcstr[j] = '\0';
487 snprintretval = snprintf((char *)fmtdbuf, SIZEOF_FMTDBUF,
488 (char *)smallfmtcstr, ((pPmInt_t)pobj)->val);
489 break;
492 #ifdef HAVE_FLOAT
493 else if (fmtcstr[i] == 'f')
495 smallfmtcstr[j] = '\0';
496 snprintretval = snprintf((char *)fmtdbuf, SIZEOF_FMTDBUF,
497 (char *)smallfmtcstr, ((pPmFloat_t)pobj)->val);
498 break;
500 #endif /* HAVE_FLOAT */
502 else if (fmtcstr[i] == 's')
504 smallfmtcstr[j] = '\0';
505 snprintretval = snprintf((char *)fmtdbuf, SIZEOF_FMTDBUF,
506 (char *)smallfmtcstr, ((pPmString_t)pobj)->val);
507 break;
511 /* Copy formatted C string into new string object */
512 for (j = 0; j < snprintretval; j++)
514 pnewstr->val[strindex++] = fmtdbuf[j];
517 pnewstr->val[strindex] = '\0';
519 #if USE_STRING_CACHE
520 /* Check for twin string in cache */
521 for (pcacheentry = pstrcache;
522 pcacheentry != C_NULL; pcacheentry = pcacheentry->next)
524 /* If string already exists */
525 if (string_compare(pcacheentry, pnewstr) == C_SAME)
527 /* Free the string */
528 retval = heap_freeChunk((pPmObj_t)pnewstr);
530 /* Return ptr to old */
531 *r_pstring = (pPmObj_t)pcacheentry;
532 return retval;
536 /* Insert string obj into cache */
537 pnewstr->next = pstrcache;
538 pstrcache = pnewstr;
540 #endif /* USE_STRING_CACHE */
542 *r_pstring = (pPmObj_t)pnewstr;
543 return PM_RET_OK;
545 #endif /* HAVE_STRING_FORMAT */