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.
17 #define __FILE_ID__ 0x12
22 * \brief String Object Type
24 * String object type opeartions.
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
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
;
52 pPmString_t pcacheentry
= C_NULL
;
53 #endif /* USE_STRING_CACHE */
56 /* If loading from an image, get length from the image */
59 len
= mem_getWord(memspace
, paddr
);
62 /* If loading from a C string, get its strlen (first null) */
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
);
82 mem_copy(memspace
, &pdst
, &psrc
, len
);
85 /* Be sure paddr points to one byte past the end of the source string */
88 /* Zero-pad end of string */
89 for (; pdst
< (uint8_t *)pstr
+ OBJ_GET_SIZE(pstr
); pdst
++)
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
;
111 /* Insert string obj into cache */
112 pstr
->next
= pstrcache
;
115 #endif /* USE_STRING_CACHE */
117 *r_pstring
= (pPmObj_t
)pstr
;
123 string_newFromChar(uint8_t const c
, pPmObj_t
*r_pstring
)
127 uint8_t const *pcstr
;
133 retval
= string_new(&pcstr
, r_pstring
);
135 /* If c was a null character, force the length to 1 */
138 ((pPmString_t
)*r_pstring
)->length
= 1;
146 string_compare(pPmString_t pstr1
, pPmString_t pstr2
)
148 /* Return false if lengths are not equal */
149 if (pstr1
->length
!= pstr2
->length
)
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
;
163 string_printFormattedBytes(uint8_t *pb
, uint8_t is_escaped
, uint16_t n
)
168 PmReturn_t retval
= PM_RET_OK
;
172 retval
= plat_putByte('\'');
173 PM_RETURN_IF_ERROR(retval
);
176 for (i
= 0; i
< n
; 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 */
188 && ((ch
< (uint8_t)32) || (ch
>= (uint8_t)128) || (ch
== '\'')))
193 nibble
= (ch
>> (uint8_t)4) + '0';
195 nibble
+= ('a' - '0' - (uint8_t)10);
196 plat_putByte(nibble
);
198 nibble
= (ch
& (uint8_t)0x0F) + '0';
200 nibble
+= ('a' - '0' - (uint8_t)10);
201 plat_putByte(nibble
);
205 /* Simply output character */
206 retval
= plat_putByte(ch
);
207 PM_RETURN_IF_ERROR(retval
);
212 retval
= plat_putByte('\'');
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
);
233 retval
= string_printFormattedBytes(&(((pPmString_t
)pstr
)->val
[0]),
235 ((pPmString_t
)pstr
)->length
);
239 #endif /* HAVE_PRINT */
243 string_cacheInit(void)
253 string_getCache(pPmString_t
**r_ppstrcache
)
256 *r_ppstrcache
= &pstrcache
;
258 *r_ppstrcache
= C_NULL
;
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
;
272 pPmString_t pcacheentry
= C_NULL
;
273 #endif /* USE_STRING_CACHE */
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
);
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
);
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
;
310 /* Insert string obj into cache */
311 pstr
->next
= pstrcache
;
313 #endif /* USE_STRING_CACHE */
315 *r_pstring
= (pPmObj_t
)pstr
;
320 #ifdef HAVE_STRING_FORMAT
322 #define SIZEOF_FMTDBUF 42
323 #define SIZEOF_SMALLFMT 8
326 string_format(pPmString_t pstr
, pPmObj_t parg
, pPmObj_t
*r_pstring
)
329 uint16_t strsize
= 0;
332 uint8_t smallfmtcstr
[SIZEOF_SMALLFMT
];
333 uint8_t fmtdbuf
[SIZEOF_FMTDBUF
];
336 uint8_t argtupleindex
= 0;
339 uint8_t expectedargcount
= 0;
343 pPmString_t pcacheentry
= C_NULL
;
344 #endif /* USE_STRING_CACHE */
346 /* Get the first arg */
349 /* Calculate the size of the resulting string */
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
++];
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
];
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
);
383 smallfmtcstr
[j
] = '\0';
384 snprintretval
= snprintf((char *)fmtdbuf
, SIZEOF_FMTDBUF
,
385 (char *)smallfmtcstr
, ((pPmInt_t
)pobj
)->val
);
390 else if (fmtcstr
[i
] == 'f')
392 if (OBJ_GET_TYPE(pobj
) != OBJ_TYPE_FLT
)
394 PM_RAISE(retval
, PM_RET_EX_TYPE
);
397 smallfmtcstr
[j
] = '\0';
398 snprintretval
= snprintf((char *)fmtdbuf
, SIZEOF_FMTDBUF
,
399 (char *)smallfmtcstr
, ((pPmFloat_t
)pobj
)->val
);
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
);
411 smallfmtcstr
[j
] = '\0';
412 snprintretval
= snprintf((char *)fmtdbuf
, SIZEOF_FMTDBUF
,
413 (char *)smallfmtcstr
, ((pPmString_t
)pobj
)->val
);
418 /* Raise ValueError if the format string was bad */
419 if (snprintretval
< 0)
421 PM_RAISE(retval
, PM_RET_EX_VAL
);
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
);
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 */
451 for (i
= 0; i
< pstr
->length
; i
++)
453 /* Copy non-format chars */
454 if (fmtcstr
[i
] != '%')
456 pnewstr
->val
[strindex
++] = fmtcstr
[i
];
460 /* If double percents, copy one percent */
461 if (fmtcstr
[++i
] == '%')
463 pnewstr
->val
[strindex
++] = '%';
467 /* Get arg from the tuple */
468 if (OBJ_GET_TYPE(parg
) == OBJ_TYPE_TUP
)
470 pobj
= ((pPmTuple_t
)parg
)->val
[argtupleindex
++];
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
];
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
);
493 else if (fmtcstr
[i
] == 'f')
495 smallfmtcstr
[j
] = '\0';
496 snprintretval
= snprintf((char *)fmtdbuf
, SIZEOF_FMTDBUF
,
497 (char *)smallfmtcstr
, ((pPmFloat_t
)pobj
)->val
);
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
);
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';
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
;
536 /* Insert string obj into cache */
537 pnewstr
->next
= pstrcache
;
540 #endif /* USE_STRING_CACHE */
542 *r_pstring
= (pPmObj_t
)pnewstr
;
545 #endif /* HAVE_STRING_FORMAT */