87 functions analysed · 71 typed · 10 untypeable · 6 timed out · 4 entry points (3 typed) · checker unknown
int ascii_width(int32_t ch, int flags)
{
// handle control characters
if (ch <= 0x1F || ch == 0x7F) {
if (!(flags & UTF8LITE_ESCAPE_CONTROL)) {
return -1;
}
switch (ch) {
case '\a':
case '\v':
// \u0007, \u000b (JSON) : \a, \b (C)
return (flags & UTF8LITE_ENCODE_JSON) ? 6 : 2;
case '\b':
case '\f':
case '\n':
case '\r':
case '\t':
return 2;
default:
return 6; // \uXXXX
}
}
// handle printable characters
switch (ch) {
case '\"':
return (flags & UTF8LITE_ESCAPE_DQUOTE) ? 2 : 1;
case '\'':
return (flags & UTF8LITE_ESCAPE_SQUOTE) ? 2 : 1;
case '\\':
if (flags & (UTF8LITE_ESCAPE_CONTROL
| UTF8LITE_ESCAPE_DQUOTE
| UTF8LITE_ESCAPE_SQUOTE
| UTF8LITE_ESCAPE_EXTENDED
| UTF8LITE_ESCAPE_UTF8)) {
return 2;
} else {
return 1;
}
default:
return 1;
}
}int assign_esc(struct utf8lite_text *text, const uint8_t *ptr, size_t size,
struct utf8lite_message *msg)
{
const uint8_t *input = ptr;
const uint8_t *end = ptr + size;
size_t attr = 0;
uint_fast8_t ch;
int err;
text->ptr = (uint8_t *)ptr;
while (ptr != end) {
ch = *ptr++;
if (ch == '\\') {
attr |= UTF8LITE_TEXT_ESC_BIT;
if ((err = utf8lite_scan_escape(&ptr, end, msg))) {
goto error;
}
} else if (ch & 0x80) {
ptr--;
if ((err = utf8lite_scan_utf8(&ptr, end, msg))) {
goto error;
}
}
}
attr |= size;
text->attr = attr;
return 0;
error:
append_location(msg, (size_t)(ptr - input));
return err;
}void assign_esc_unsafe(struct utf8lite_text *text, const uint8_t *ptr,
size_t size)
{
const uint8_t *end = ptr + size;
size_t attr = 0;
int32_t code;
uint_fast8_t ch;
text->ptr = (uint8_t *)ptr;
while (ptr != end) {
ch = *ptr++;
if (ch == '\\') {
attr |= UTF8LITE_TEXT_ESC_BIT;
ch = *ptr++;
switch (ch) {
case 'u':
utf8lite_decode_uescape(&ptr, &code);
break;
default:
break;
}
} else if (ch & 0x80) {
ptr += UTF8LITE_UTF8_TAIL_LEN(ch);
}
}
attr |= size;
text->attr = attr;
}int assign_raw(struct utf8lite_text *text, const uint8_t *ptr, size_t size,
struct utf8lite_message *msg)
{
const uint8_t *input = ptr;
const uint8_t *end = ptr + size;
uint_fast8_t ch;
int err;
text->ptr = (uint8_t *)ptr;
while (ptr != end) {
ch = *ptr++;
if (ch & 0x80) {
ptr--;
if ((err = utf8lite_scan_utf8(&ptr, end, msg))) {
goto error;
}
}
}
text->attr = size;
return 0;
error:
append_location(msg, (size_t)(ptr - input));
text->ptr = NULL;
text->attr = 0;
return err;
}void assign_raw_unsafe(struct utf8lite_text *text, const uint8_t *ptr,
size_t size)
{
const uint8_t *end = ptr + size;
uint_fast8_t ch;
text->ptr = (uint8_t *)ptr;
while (ptr != end) {
ch = *ptr++;
if (ch & 0x80) {
ptr += UTF8LITE_UTF8_TAIL_LEN(ch);
}
}
text->attr = size;
}int byte_width(uint8_t byte, int flags)
{
if (byte < 0x80) {
switch (byte) {
case '\a':
case '\b':
case '\f':
case '\n':
case '\r':
case '\t':
case '\v':
return (flags & UTF8LITE_ESCAPE_CONTROL) ? 2 : -1;
case '\\':
return (flags & (UTF8LITE_ESCAPE_CONTROL
| UTF8LITE_ESCAPE_DQUOTE)
? 2 : 1);
case '"':
return (flags & UTF8LITE_ESCAPE_DQUOTE) ? 2 : 1;
default:
if (isprint((int)byte)) {
return 1;
}
break;
}
}
// \xXX non-ASCII or non-printable byte
return (flags & UTF8LITE_ESCAPE_CONTROL) ? 4 : -1;
}void bytes_init(struct rutf8_bytes *bytes, SEXP charsxp)
{
assert(charsxp != NA_STRING);
bytes->ptr = (const uint8_t *)CHAR(charsxp);
bytes->size = (size_t)XLENGTH(charsxp);
}static int charwidth(int32_t code)
{
const int32_t block_size = 128;
uint8_t i = charwidth_stage1[code / block_size];
return charwidth_stage2[i][code % block_size];
}static const char *encoding_name(cetype_t ce)
{
switch (ce) {
case CE_LATIN1:
return "latin1";
case CE_UTF8:
return "UTF-8";
case CE_SYMBOL:
return "symbol";
case CE_BYTES:
return "bytes";
case CE_ANY:
case CE_NATIVE:
default:
return "unknown";
}
}static int maybe_indent(struct utf8lite_render *r)
{
int ntab = r->indent;
char *end;
int i;
if (!r->needs_indent) {
return 0;
}
for (i = 0; i < ntab; i++) {
utf8lite_render_grow(r, r->tab_length);
CHECK_ERROR(r);
end = r->string + r->length;
memcpy(end, r->tab, r->tab_length + 1); // include '\0'
r->length += r->tab_length;
}
r->needs_indent = 0;
return 0;
}void render_byte(struct utf8lite_render *r, uint8_t byte)
{
char ch, buf[5];
int err = 0;
if (byte <= 0x1f || byte >= 0x7f) {
if (r->flags & UTF8LITE_ESCAPE_CONTROL) {
switch (byte) {
case '\a':
TRY(utf8lite_render_raw(r, "\\a", 2));
break;
case '\b':
TRY(utf8lite_render_raw(r, "\\b", 2));
break;
case '\f':
TRY(utf8lite_render_raw(r, "\\f", 2));
break;
case '\n':
TRY(utf8lite_render_raw(r, "\\n", 2));
break;
case '\r':
TRY(utf8lite_render_raw(r, "\\r", 2));
break;
case '\t':
TRY(utf8lite_render_raw(r, "\\t", 2));
break;
case '\v':
TRY(utf8lite_render_raw(r, "\\v", 2));
break;
default:
snprintf(buf, sizeof(buf), "\\x%02x", (unsigned)byte);
TRY(utf8lite_render_raw(r, buf, 4));
break;
}
} else {
ch = (char)byte;
TRY(utf8lite_render_raw(r, &ch, 1));
}
} else {
buf[0] = (char)byte;
buf[1] = '\0';
TRY(utf8lite_render_string(r, buf));
}
exit:
CHECK_ERROR(err);
}int rutf8_as_justify(SEXP justify)
{
const char *str;
str = CHAR(STRING_ELT(justify, 0));
if (strcmp(str, "left") == 0) {
return RUTF8_JUSTIFY_LEFT;
} else if (strcmp(str, "right") == 0) {
return RUTF8_JUSTIFY_RIGHT;
} else if (strcmp(str, "centre") == 0) {
return RUTF8_JUSTIFY_CENTRE;
} else {
return RUTF8_JUSTIFY_NONE;
}
}struct utf8lite_render *rutf8_as_render(SEXP x)
{
struct rutf8_render *obj;
if (!rutf8_is_render(x)) {
error("invalid render object");
}
obj = R_ExternalPtrAddr(x);
return (obj->has_render) ? &obj->render : NULL;
}const char *rutf8_as_style(SEXP style)
{
SEXP elt;
char *ans;
int n;
if (style == R_NilValue) {
return NULL;
}
PROTECT(elt = STRING_ELT(style, 0));
n = LENGTH(elt);
ans = R_alloc(2 + n + 2, 1);
ans[0] = '\033';
ans[1] = '[';
memcpy(ans + 2, CHAR(elt), n);
ans[2 + n] = 'm';
ans[3 + n] = '\0';
UNPROTECT(1);
return ans;
}SEXP rutf8_as_utf8(SEXP sx)
{
SEXP ans, sstr;
PROTECT_INDEX ipx;
struct utf8lite_message msg;
struct utf8lite_text text;
const uint8_t *str;
cetype_t ce;
size_t size;
R_xlen_t i, n;
int nprot = 0, duped = 0, raw;
if (sx == R_NilValue) {
return R_NilValue;
}
if (!isString(sx)) {
error("argument is not a character object");
}
// reserve a protection slot for ans in case we need to duplicate
PROTECT_WITH_INDEX(ans = sx, &ipx); nprot++;
n = XLENGTH(sx);
for (i = 0; i < n; i++) {
CHECK_INTERRUPT(i);
PROTECT(sstr = STRING_ELT(sx, i)); nprot++;
if (sstr == NA_STRING) {
UNPROTECT(1); nprot--;
continue;
}
ce = getCharCE(sstr);
raw = rutf8_encodes_utf8(ce) || ce == CE_BYTES;
if (raw) {
str = (const uint8_t *)CHAR(sstr);
size = (size_t)XLENGTH(sstr);
} else {
str = (const uint8_t *)rutf8_translate_utf8(sstr);
size = strlen((const char *)str);
}
if (utf8lite_text_assign(&text, str, size, 0, &msg)) {
if (ce == CE_BYTES) {
Rf_error("entry %"PRIu64
" cannot be converted from \"bytes\""
" Encoding to \"UTF-8\"; %s",
(uint64_t)i + 1, msg.string);
} else if (raw) {
Rf_error("entry %"PRIu64
" has wrong Encoding;"
" marked as \"UTF-8\""
" but %s",
(uint64_t)i + 1,
msg.string);
} else {
Rf_error("entry %"PRIu64
" cannot be converted"
" from \"%s\" Encoding to \"UTF-8\";"
" %s in converted string",
(uint64_t)i + 1,
encoding_name(ce),
msg.string);
}
}
if (!raw || ce == CE_BYTES || ce == CE_NATIVE) {
if (!duped) {
REPROTECT(ans = duplicate(ans), ipx);
duped = 1;
}
SET_STRING_ELT(ans, i,
mkCharLenCE((const char *)str,
(int)size, CE_UTF8));
}
UNPROTECT(1); nprot--;
}
UNPROTECT(nprot);
return ans;
}void rutf8_bytes_render(struct utf8lite_render *r,
const struct rutf8_bytes *bytes,
int width, int quote, enum rutf8_justify_type justify)
{
int centre;
if (justify == RUTF8_JUSTIFY_RIGHT) {
rutf8_bytes_rrender(r, bytes, width, quote);
} else {
centre = (justify == RUTF8_JUSTIFY_CENTRE);
rutf8_bytes_lrender(r, bytes, width, quote, centre);
}
}static void rutf8_bytes_rrender(struct utf8lite_render *r,
const struct rutf8_bytes *bytes,
int width_min, int quote)
{
const uint8_t *ptr, *end;
uint8_t byte;
int err = 0, fullwidth, quotes;
assert(width_min >= 0);
quotes = quote ? 2 : 0;
if (width_min > 0) {
fullwidth = rutf8_bytes_width(bytes, r->flags);
// ensure fullwidth + quotes doesn't overflow
if (fullwidth <= width_min - quotes) {
fullwidth += quotes;
TRY(utf8lite_render_chars(r, ' ',
width_min - fullwidth));
}
}
if (quote) {
TRY(utf8lite_render_raw(r, "\"", 1));
}
ptr = bytes->ptr;
end = bytes->ptr + bytes->size;
while (ptr < end) {
byte = *ptr++;
render_byte(r, byte);
}
if (quote) {
TRY(utf8lite_render_raw(r, "\"", 1));
}
exit:
CHECK_ERROR(err);
}int rutf8_bytes_width(const struct rutf8_bytes *bytes, int flags)
{
const uint8_t *ptr = bytes->ptr;
const uint8_t *end = ptr + bytes->size;
uint8_t byte;
int width, w;
width = 0;
while (ptr != end) {
byte = *ptr++;
w = byte_width(byte, flags);
if (w < 0) {
return -1;
}
if (width > INT_MAX - w) {
Rf_error("width exceeds maximum (%d)", INT_MAX);
}
width += w;
}
return width;
}int rutf8_encodes_utf8(cetype_t ce)
{
switch (ce) {
case CE_ANY:
case CE_UTF8:
#if (!defined(_WIN32) && !defined(_WIN64))
case CE_NATIVE: // assume that 'native' is UTF-8 on non-Windows
#endif
return 1;
default:
return 0;
}
}void rutf8_free_render(SEXP x)
{
struct rutf8_render *obj = R_ExternalPtrAddr(x);
R_SetExternalPtrAddr(x, NULL);
if (obj) {
if (obj->has_render) {
utf8lite_render_destroy(&obj->render);
}
free(obj);
}
}int rutf8_is_render(SEXP x)
{
return ((TYPEOF(x) == EXTPTRSXP)
&& (R_ExternalPtrTag(x) == RENDER_TAG));
}void rutf8_string_init(struct rutf8_string *str, SEXP charsxp)
{
if (charsxp == NA_STRING) {
str->type = RUTF8_STRING_NONE;
} else if (!text_init(&str->value.text, charsxp)) {
str->type = RUTF8_STRING_TEXT;
} else {
bytes_init(&str->value.bytes, charsxp);
str->type = RUTF8_STRING_BYTES;
}
}void rutf8_string_render(struct utf8lite_render *r,
const struct rutf8_string *str,
int width, int quote, enum rutf8_justify_type justify)
{
switch (str->type) {
case RUTF8_STRING_TEXT:
rutf8_text_render(r, &str->value.text, width, quote, justify);
break;
case RUTF8_STRING_BYTES:
rutf8_bytes_render(r, &str->value.bytes, width, quote, justify);
break;
default:
break;
}
}int rutf8_string_width(const struct rutf8_string *str, int flags)
{
switch (str->type) {
case RUTF8_STRING_TEXT:
return rutf8_text_width(&str->value.text, flags);
case RUTF8_STRING_BYTES:
return rutf8_bytes_width(&str->value.bytes, flags);
default:
return -1;
}
}static void rutf8_text_lrender(struct utf8lite_render *r,
const struct utf8lite_text *text,
int width_min, int quote, int centre)
{
struct utf8lite_graphscan scan;
int err = 0, w, fullwidth, width, quotes;
assert(width_min >= 0);
quotes = quote ? 2 : 0;
width = 0;
if (centre && width_min > 0) {
fullwidth = rutf8_text_width(text, r->flags);
// ensure fullwidth + quotes doesn't overflow
if (fullwidth <= width_min - quotes) {
width = (width_min - fullwidth - quotes) / 2;
TRY(utf8lite_render_chars(r, ' ', width));
}
}
if (quote) {
TRY(utf8lite_render_raw(r, "\"", 1));
assert(width < INT_MAX); // width <= width_min / 2
width++;
}
utf8lite_graphscan_make(&scan, text);
while (utf8lite_graphscan_advance(&scan)) {
TRY(utf8lite_graph_measure(&scan.current, r->flags, &w));
TRY(utf8lite_render_graph(r, &scan.current));
assert(w >= 0);
if (width <= width_min - w) {
width += w;
} else {
width = width_min; // truncate to avoid overflow
}
}
if (quote) {
TRY(utf8lite_render_raw(r, "\"", 1));
if (width < width_min) { // avoid overflow
width++;
}
}
TRY(utf8lite_render_chars(r, ' ', width_min - width));
exit:
CHECK_ERROR(err);
}void rutf8_text_render(struct utf8lite_render *r,
const struct utf8lite_text *text,
int width, int quote, enum rutf8_justify_type justify)
{
int centre;
if (justify == RUTF8_JUSTIFY_RIGHT) {
rutf8_text_rrender(r, text, width, quote);
} else {
centre = (justify == RUTF8_JUSTIFY_CENTRE);
rutf8_text_lrender(r, text, width, quote, centre);
}
}static void rutf8_text_rrender(struct utf8lite_render *r,
const struct utf8lite_text *text,
int width_min, int quote)
{
struct utf8lite_graphscan scan;
int err = 0, fullwidth, quotes;
quotes = quote ? 2 : 0;
if (width_min > 0) {
fullwidth = rutf8_text_width(text, r->flags);
// ensure fullwidth + quotes doesn't overflow
if (fullwidth <= width_min - quotes) {
fullwidth += quotes;
TRY(utf8lite_render_chars(r, ' ',
width_min - fullwidth));
}
}
if (quote) {
TRY(utf8lite_render_raw(r, "\"", 1));
}
utf8lite_graphscan_make(&scan, text);
while (utf8lite_graphscan_advance(&scan)) {
TRY(utf8lite_render_graph(r, &scan.current));
}
if (quote) {
TRY(utf8lite_render_raw(r, "\"", 1));
}
exit:
CHECK_ERROR(err);
}int rutf8_text_width(const struct utf8lite_text *text, int flags)
{
struct utf8lite_graphscan scan;
int err = 0, width, w;
utf8lite_graphscan_make(&scan, text);
width = 0;
while (utf8lite_graphscan_advance(&scan)) {
TRY(utf8lite_graph_measure(&scan.current, flags, &w));
if (w < 0) {
return -1;
}
if (width > INT_MAX - w) {
Rf_error("width exceeds maximum (%d)", INT_MAX);
}
width += w;
}
exit:
CHECK_ERROR(err);
return width;
}const char *rutf8_translate_utf8(SEXP x)
{
return translateCharUTF8(x);
}SEXP rutf8_utf8_valid(SEXP sx)
{
SEXP ans, sstr;
const uint8_t *str;
struct utf8lite_text text;
cetype_t ce;
size_t size;
R_xlen_t i, n;
int raw, val;
if (sx == R_NilValue) {
return R_NilValue;
}
if (!isString(sx)) {
error("argument is not a character object");
}
n = XLENGTH(sx);
PROTECT(ans = allocVector(LGLSXP, n));
setAttrib(ans, R_NamesSymbol, getAttrib(sx, R_NamesSymbol));
setAttrib(ans, R_DimSymbol, getAttrib(sx, R_DimSymbol));
setAttrib(ans, R_DimNamesSymbol, getAttrib(sx, R_DimNamesSymbol));
n = XLENGTH(sx);
for (i = 0; i < n; i++) {
CHECK_INTERRUPT(i);
PROTECT(sstr = STRING_ELT(sx, i));
if (sstr == NA_STRING) {
LOGICAL(ans)[i] = NA_LOGICAL;
UNPROTECT(1);
continue;
}
ce = getCharCE(sstr);
raw = rutf8_encodes_utf8(ce) || ce == CE_BYTES;
if (raw) {
str = (const uint8_t *)CHAR(sstr);
size = (size_t)XLENGTH(sstr);
} else {
str = (const uint8_t *)rutf8_translate_utf8(sstr);
size = strlen((const char *)str);
}
if (utf8lite_text_assign(&text, str, size, 0, NULL)) {
val = FALSE;
} else {
val = TRUE;
}
LOGICAL(ans)[i] = val;
UNPROTECT(1);
}
UNPROTECT(1);
return ans;
}SEXP rutf8_utf8_width(SEXP sx, SEXP sencode, SEXP squote, SEXP sutf8)
{
SEXP ans, selt;
struct rutf8_string elt;
R_xlen_t i, n;
int flags, encode, quote, quotes, utf8, w;
if (sx == R_NilValue) {
return R_NilValue;
}
if (!isString(sx)) {
error("argument is not a character object");
}
n = XLENGTH(sx);
encode = LOGICAL(sencode)[0] == TRUE;
quote = LOGICAL(squote)[0] == TRUE;
utf8 = LOGICAL(sutf8)[0] == TRUE;
flags = UTF8LITE_ENCODE_C;
if (encode) {
flags |= UTF8LITE_ESCAPE_CONTROL;
if (!utf8) {
flags |= UTF8LITE_ESCAPE_UTF8;
}
#if defined(_WIN32) || defined(_WIN64)
flags |= UTF8LITE_ESCAPE_EXTENDED;
#endif
}
if (quote) {
flags |= UTF8LITE_ESCAPE_DQUOTE;
}
quotes = quote ? 2 : 0;
PROTECT(ans = allocVector(INTSXP, n));
setAttrib(ans, R_NamesSymbol, getAttrib(sx, R_NamesSymbol));
setAttrib(ans, R_DimSymbol, getAttrib(sx, R_DimSymbol));
setAttrib(ans, R_DimNamesSymbol, getAttrib(sx, R_DimNamesSymbol));
for (i = 0; i < n; i++) {
CHECK_INTERRUPT(i);
PROTECT(selt = STRING_ELT(sx, i));
rutf8_string_init(&elt, selt);
if (elt.type == RUTF8_STRING_NONE) {
w = NA_INTEGER;
} else if (elt.type == RUTF8_STRING_TEXT && !encode && !utf8
&& !utf8lite_text_isascii(&elt.value.text)) {
w = NA_INTEGER;
} else {
w = rutf8_string_width(&elt, flags);
if (w < 0) {
w = NA_INTEGER;
} else if (w > INT_MAX - quotes) {
Rf_error("width exceeds maximum (%d)",
INT_MAX);
} else {
w += quotes;
}
}
INTEGER(ans)[i] = w;
UNPROTECT(1);
}
UNPROTECT(1);
return ans;
}static int style_close(struct utf8lite_render *r)
{
if (!r->style_close_length) {
return 0;
}
return utf8lite_render_raw(r, r->style_close, r->style_close_length);
}static int style_open(struct utf8lite_render *r)
{
if (!r->style_open_length) {
return 0;
}
return utf8lite_render_raw(r, r->style_open, r->style_open_length);
}int utf8_escape_width(int32_t ch, int flags)
{
if (ch <= 0xFFFF) {
return 6;
} else if (flags & UTF8LITE_ENCODE_JSON) {
return 12;
} else {
return 10;
}
}int utf8_width(int32_t ch, int cw, int flags)
{
int w = -1;
switch ((enum utf8lite_charwidth_type)cw) {
case UTF8LITE_CHARWIDTH_NONE:
if (flags & UTF8LITE_ESCAPE_CONTROL) {
w = utf8_escape_width(ch, flags);
} else {
w = -1;
}
break;
case UTF8LITE_CHARWIDTH_IGNORABLE:
case UTF8LITE_CHARWIDTH_MARK:
w = 0;
break;
case UTF8LITE_CHARWIDTH_NARROW:
w = 1;
break;
case UTF8LITE_CHARWIDTH_AMBIGUOUS:
w = (flags & UTF8LITE_ENCODE_AMBIGWIDE) ? 2 : 1;
break;
case UTF8LITE_CHARWIDTH_WIDE:
case UTF8LITE_CHARWIDTH_EMOJI:
w = 2;
break;
}
return w;
}int utf8lite_array_grow(void **baseptr, int *sizeptr, size_t width, int count,
int nadd)
{
void *base = *baseptr;
int size = *sizeptr;
int err;
assert(0 <= count);
assert(count <= size);
assert(width > 0);
if (nadd <= size - count) {
return 0;
}
if ((err = utf8lite_array_size_add(&size, width, count, nadd))) {
return err;
}
if (!(base = realloc(base, ((size_t)size) * width))) {
err = UTF8LITE_ERROR_NOMEM;
return err;
}
*baseptr = base;
*sizeptr = size;
return 0;
}int utf8lite_array_size_add(int *sizeptr, size_t width, int count, int nadd)
{
size_t size, size_min, size_max;
int err;
assert(*sizeptr >= 0);
assert(count >= 0);
assert(nadd >= 0);
if (width == 0) {
return 0;
}
size = (size_t)*sizeptr;
if ((err = utf8lite_bigarray_size_add(&size, width, (size_t)count,
(size_t)nadd))) {
return err;
}
size_max = (size_t)INT_MAX / width;
if (size > size_max) {
size = size_max;
size_min = (size_t)count + (size_t)nadd;
if (size < size_min) {
err = UTF8LITE_ERROR_OVERFLOW;
//utf8lite_log(err, "array size (%"PRIu64
// " elements of %"PRIu64" bytes each)"
// " exceeds maximum (%"PRIu64" elements)",
// (uint64_t)size_min, (uint64_t)width,
// (uint64_t)size_max);
return err;
}
}
*sizeptr = (int)size;
return 0;
}int utf8lite_bigarray_size_add(size_t *sizeptr, size_t width, size_t count,
size_t nadd)
{
size_t size = *sizeptr;
size_t size_min;
int err;
double n1;
if (width == 0) {
return 0;
}
if (count > (SIZE_MAX - nadd) / width) {
err = UTF8LITE_ERROR_OVERFLOW;
//utf8lite_log(err, "array size (%"PRIu64" + %"PRIu64
// " elements of %"PRIu64" bytes each)"
// " exceeds maximum (%"PRIu64" elements)",
// (uint64_t)count, (uint64_t)nadd,
// (uint64_t)width, (uint64_t)SIZE_MAX);
return err;
}
size_min = count + nadd;
if (size >= size_min) {
return 0;
}
assert(UTF8LITE_ARRAY_SIZE_INIT > 0);
assert(UTF8LITE_ARRAY_GROW > 1);
if (size < UTF8LITE_ARRAY_SIZE_INIT && size_min > 0) {
size = UTF8LITE_ARRAY_SIZE_INIT;
}
while (size < size_min) {
n1 = UTF8LITE_ARRAY_GROW * size;
if (n1 > SIZE_MAX / width) {
size = SIZE_MAX / width;
} else {
size = (size_t)n1;
}
}
*sizeptr = size;
return 0;
}void utf8lite_decode_escape(const uint8_t **inputptr, int32_t *codeptr)
{
const uint8_t *ptr = *inputptr;
int32_t code;
code = *ptr++;
switch (code) {
case 'b':
code = '\b';
break;
case 'f':
code = '\f';
break;
case 'n':
code = '\n';
break;
case 'r':
code = '\r';
break;
case 't':
code = '\t';
break;
case 'u':
*inputptr = ptr;
utf8lite_decode_uescape(inputptr, codeptr);
return;
default:
break;
}
*inputptr = ptr;
*codeptr = code;
}void utf8lite_decode_uescape(const uint8_t **inputptr, int32_t *codeptr)
{
const uint8_t *ptr = *inputptr;
int32_t code;
uint_fast16_t low;
uint_fast8_t ch;
unsigned i;
code = 0;
for (i = 0; i < 4; i++) {
ch = *ptr++;
code = (code << 4) + hextoi(ch);
}
if (UTF8LITE_IS_UTF16_HIGH(code)) {
// skip over \u
ptr += 2;
low = 0;
for (i = 0; i < 4; i++) {
ch = *ptr++;
low = (uint_fast16_t)(low << 4) + hextoi(ch);
}
code = UTF8LITE_DECODE_UTF16_PAIR(code, low);
}
*codeptr = code;
*inputptr = ptr;
}void utf8lite_encode_utf8(int32_t code, uint8_t **bufptr)
{
uint8_t *ptr = *bufptr;
int32_t x = code;
if (x <= 0x7F) {
*ptr++ = (uint8_t)x;
} else if (x <= 0x07FF) {
*ptr++ = (uint8_t)(0xC0 | (x >> 6));
*ptr++ = (uint8_t)(0x80 | (x & 0x3F));
} else if (x <= 0xFFFF) {
*ptr++ = (uint8_t)(0xE0 | (x >> 12));
*ptr++ = (uint8_t)(0x80 | ((x >> 6) & 0x3F));
*ptr++ = (uint8_t)(0x80 | (x & 0x3F));
} else {
*ptr++ = (uint8_t)(0xF0 | (x >> 18));
*ptr++ = (uint8_t)(0x80 | ((x >> 12) & 0x3F));
*ptr++ = (uint8_t)(0x80 | ((x >> 6) & 0x3F));
*ptr++ = (uint8_t)(0x80 | (x & 0x3F));
}
*bufptr = ptr;
}static int utf8lite_escape_ascii(struct utf8lite_render *r, int32_t ch)
{
style_open(r);
CHECK_ERROR(r);
// character expansion for a special escape: \uXXXX or \X
utf8lite_render_grow(r, 6);
CHECK_ERROR(r);
if (ch <= 0x1F || ch == 0x7F) {
switch (ch) {
case '\a':
if (r->flags & UTF8LITE_ENCODE_JSON) {
r->length += snprintf(&r->string[r->length], r->length_max - r->length + 1,
"\\u%04x", (unsigned)ch);
} else {
r->string[r->length++] = '\\';
r->string[r->length++] = 'a';
r->string[r->length] = '\0';
}
break;
case '\b':
r->string[r->length++] = '\\';
r->string[r->length++] = 'b';
r->string[r->length] = '\0';
break;
case '\f':
r->string[r->length++] = '\\';
r->string[r->length++] = 'f';
r->string[r->length] = '\0';
break;
case '\n':
r->string[r->length++] = '\\';
r->string[r->length++] = 'n';
r->string[r->length] = '\0';
break;
case '\r':
r->string[r->length++] = '\\';
r->string[r->length++] = 'r';
r->string[r->length] = '\0';
break;
case '\t':
r->string[r->length++] = '\\';
r->string[r->length++] = 't';
r->string[r->length] = '\0';
break;
case '\v':
if (r->flags & UTF8LITE_ENCODE_JSON) {
r->length += snprintf(&r->string[r->length], r->length_max - r->length + 1,
"\\u%04x", (unsigned)ch);
} else {
r->string[r->length++] = '\\';
r->string[r->length++] = 'v';
r->string[r->length] = '\0';
}
break;
default:
r->length += snprintf(&r->string[r->length], r->length_max - r->length + 1,
"\\u%04x", (unsigned)ch);
break;
}
style_close(r);
CHECK_ERROR(r);
} else {
r->string[r->length++] = '\\';
r->string[r->length] = '\0';
style_close(r);
CHECK_ERROR(r);
utf8lite_render_grow(r, 1);
CHECK_ERROR(r);
r->string[r->length++] = (char)ch;
r->string[r->length] = '\0';
}
return 0;
}static int utf8lite_escape_utf8(struct utf8lite_render *r, int32_t ch)
{
unsigned hi, lo;
int len;
style_open(r);
CHECK_ERROR(r);
if (ch <= 0xFFFF) {
// \uXXXX
len = 6;
} else if (r->flags & UTF8LITE_ENCODE_JSON) {
// \uXXXX\uYYYY
len = 12;
} else {
// \UXXXXYYYY
len = 10;
}
utf8lite_render_grow(r, len);
CHECK_ERROR(r);
if (ch <= 0xFFFF) {
r->length += snprintf(&r->string[r->length], r->length_max - r->length + 1,
"\\u%04x", (unsigned)ch);
} else if (r->flags & UTF8LITE_ENCODE_JSON) {
hi = UTF8LITE_UTF16_HIGH(ch);
lo = UTF8LITE_UTF16_LOW(ch);
r->length += snprintf(&r->string[r->length], r->length_max - r->length + 1,
"\\u%04x\\u%04x", hi, lo);
} else {
r->length += snprintf(&r->string[r->length], r->length_max - r->length + 1,
"\\U%08"PRIx32, (uint32_t)ch);
}
style_close(r);
CHECK_ERROR(r);
return 0;
}int utf8lite_graph_measure(const struct utf8lite_graph *g,
int flags, int *widthptr)
{
struct utf8lite_text_iter it;
int32_t ch;
int err = 0, cw, w, width;
width = 0;
utf8lite_text_iter_make(&it, &g->text);
while (utf8lite_text_iter_advance(&it)) {
ch = it.current;
if (ch <= 0x7F) {
w = ascii_width(ch, flags);
} else if (flags & UTF8LITE_ESCAPE_UTF8) {
w = utf8_escape_width(ch, flags);
} else if ((flags & UTF8LITE_ESCAPE_EXTENDED)
&& (ch > 0xFFFF)) {
w = utf8_escape_width(ch, flags);
} else {
cw = utf8lite_charwidth(ch);
if (cw == UTF8LITE_CHARWIDTH_EMOJI) {
width = 2;
goto exit;
}
w = utf8_width(ch, cw, flags);
}
if (w < 0) {
width = w;
goto exit;
} else if (w > INT_MAX - width) {
width = -1;
err = UTF8LITE_ERROR_OVERFLOW;
goto exit;
} else {
width += w;
}
}
exit:
if (widthptr) {
*widthptr = width;
}
return err;
}void utf8lite_graphscan_make(struct utf8lite_graphscan *scan,
const struct utf8lite_text *text)
{
utf8lite_text_iter_make(&scan->iter, text);
utf8lite_graphscan_reset(scan);
}void utf8lite_graphscan_reset(struct utf8lite_graphscan *scan)
{
utf8lite_text_iter_reset(&scan->iter);
scan->current.text.ptr = (uint8_t *)scan->iter.ptr;
scan->current.text.attr = (scan->iter.text_attr
& ~UTF8LITE_TEXT_SIZE_MASK);
NEXT();
}void utf8lite_message_set(struct utf8lite_message *msg,
const char *fmt, ...)
{
va_list ap;
if (msg) {
va_start(ap, fmt);
vsnprintf(msg->string, sizeof(msg->string), fmt, ap);
va_end(ap);
}
}static int utf8lite_render_ascii(struct utf8lite_render *r, int32_t ch)
{
if ((ch <= 0x1F || ch == 0x7F)
&& (r->flags & UTF8LITE_ESCAPE_CONTROL)) {
return utf8lite_escape_ascii(r, ch);
}
switch (ch) {
case '\"':
if (r->flags & UTF8LITE_ESCAPE_DQUOTE) {
return utf8lite_escape_ascii(r, ch);
}
break;
case '\'':
if (r->flags & UTF8LITE_ESCAPE_SQUOTE) {
return utf8lite_escape_ascii(r, ch);
}
break;
case '\\':
if (r->flags & (UTF8LITE_ESCAPE_CONTROL
| UTF8LITE_ESCAPE_DQUOTE
| UTF8LITE_ESCAPE_SQUOTE
| UTF8LITE_ESCAPE_EXTENDED
| UTF8LITE_ESCAPE_UTF8)) {
return utf8lite_escape_ascii(r, ch);
}
break;
default:
break;
}
utf8lite_render_grow(r, 1);
CHECK_ERROR(r);
r->string[r->length++] = (char)ch;
r->string[r->length] = '\0';
return 0;
}int utf8lite_render_char(struct utf8lite_render *r, int32_t ch)
{
uint8_t buf[5];
uint8_t *ptr = buf;
// decode to string, then render. 'render_string' handles
// escaping, removing default ignorables, etc. if necessary
utf8lite_encode_utf8(ch, &ptr);
*ptr = '\0';
return utf8lite_render_string(r, (const char *)buf);
}int utf8lite_render_chars(struct utf8lite_render *r, int32_t ch, int nchar)
{
CHECK_ERROR(r);
while (nchar-- > 0) {
utf8lite_render_char(r, ch);
CHECK_ERROR(r);
}
return 0;
}int utf8lite_render_code(struct utf8lite_render *r, int32_t ch, int *attrptr)
{
char *end;
uint8_t *uend;
int type;
CHECK_ERROR(r);
maybe_indent(r);
CHECK_ERROR(r);
// maximum character expansion: \uXXXX\uXXXX
utf8lite_render_grow(r, 12);
CHECK_ERROR(r);
end = r->string + r->length;
if (UTF8LITE_IS_ASCII(ch)) {
return utf8lite_render_ascii(r, ch);
} else if (r->flags & UTF8LITE_ESCAPE_UTF8) {
return utf8lite_escape_utf8(r, ch);
}
if (ch > 0xFFFF) {
if (r->flags & UTF8LITE_ESCAPE_EXTENDED) {
return utf8lite_escape_utf8(r, ch);
} else {
*attrptr |= CODE_EXTENDED;
}
}
*attrptr |= CODE_UTF8;
type = utf8lite_charwidth(ch);
switch (type) {
case UTF8LITE_CHARWIDTH_NONE:
if (r->flags & UTF8LITE_ESCAPE_CONTROL) {
return utf8lite_escape_utf8(r, ch);
}
break;
case UTF8LITE_CHARWIDTH_IGNORABLE:
if ((r->flags & UTF8LITE_ENCODE_RMDI)
&& (!(*attrptr & CODE_EMOJI))) {
return 0;
}
break;
case UTF8LITE_CHARWIDTH_EMOJI:
*attrptr |= CODE_EMOJI;
break;
default:
break;
}
uend = (uint8_t *)end;
utf8lite_encode_utf8(ch, &uend);
*uend = '\0';
r->length += UTF8LITE_UTF8_ENCODE_LEN(ch);
return 0;
}void utf8lite_render_destroy(struct utf8lite_render *r)
{
free(r->string);
}int utf8lite_render_graph(struct utf8lite_render *r,
const struct utf8lite_graph *g)
{
struct utf8lite_text_iter it;
int attr = CODE_ASCII;
CHECK_ERROR(r);
utf8lite_text_iter_make(&it, &g->text);
while (utf8lite_text_iter_advance(&it)) {
utf8lite_render_code(r, it.current, &attr);
CHECK_ERROR(r);
}
if (attr & CODE_EMOJI && (r->flags & UTF8LITE_ENCODE_EMOJIZWSP)) {
utf8lite_render_raw(r, "\xE2\x80\x8B", 3); // U+200B, ZWSP
CHECK_ERROR(r);
}
return 0;
}static int utf8lite_render_grow(struct utf8lite_render *r, int nadd)
{
void *base = r->string;
int size = r->length_max + 1;
int err;
if (nadd <= 0 || r->length_max - nadd > r->length) {
return 0;
}
if ((err = utf8lite_array_grow(&base, &size, sizeof(*r->string),
r->length + 1, nadd))) {
r->error = err;
return r->error;
}
r->string = base;
r->length_max = size - 1;
return 0;
}int utf8lite_render_raw(struct utf8lite_render *r, const char *bytes,
size_t size)
{
if (size > INT_MAX) {
r->error = UTF8LITE_ERROR_OVERFLOW;
return r->error;
}
utf8lite_render_grow(r, (int)size);
CHECK_ERROR(r);
memcpy(r->string + r->length, bytes, size);
r->length += (int)size;
r->string[r->length] = '\0';
return 0;
}int utf8lite_render_text(struct utf8lite_render *r,
const struct utf8lite_text *text)
{
struct utf8lite_graphscan scan;
CHECK_ERROR(r);
utf8lite_graphscan_make(&scan, text);
while (utf8lite_graphscan_advance(&scan)) {
utf8lite_render_graph(r, &scan.current);
CHECK_ERROR(r);
}
return 0;
}int utf8lite_scan_escape(const uint8_t **bufptr, const uint8_t *end,
struct utf8lite_message *msg)
{
const uint8_t *input = *bufptr;
const uint8_t *ptr = input;
uint_fast8_t ch;
int err;
if (ptr == end) {
goto error_incomplete;
}
ch = *ptr++;
switch (ch) {
case '"':
case '\\':
case '/':
case 'b':
case 'f':
case 'n':
case 'r':
case 't':
break;
case 'u':
if ((err = utf8lite_scan_uescape(&ptr, end, msg))) {
goto out;
}
break;
default:
goto error_inval;
}
err = 0;
goto out;
error_incomplete:
err = UTF8LITE_ERROR_INVAL;
utf8lite_message_set(msg, "incomplete escape code (\\)");
goto out;
error_inval:
err = UTF8LITE_ERROR_INVAL;
utf8lite_message_set(msg, "invalid escape code (\\%c)", ch);
goto out;
out:
*bufptr = ptr;
return err;
}int utf8lite_scan_uescape(const uint8_t **bufptr, const uint8_t *end,
struct utf8lite_message *msg)
{
const uint8_t *input = *bufptr;
const uint8_t *ptr = input;
int32_t code, low;
uint_fast8_t ch;
unsigned i;
int err;
if (ptr + 4 > end) {
goto error_inval_incomplete;
}
code = 0;
for (i = 0; i < 4; i++) {
ch = *ptr++;
if (!isxdigit(ch)) {
goto error_inval_hex;
}
code = (code << 4) + hextoi(ch);
}
if (UTF8LITE_IS_UTF16_HIGH(code)) {
if (ptr + 6 > end || ptr[0] != '\\' || ptr[1] != 'u') {
goto error_inval_nolow;
}
ptr += 2;
input = ptr;
low = 0;
for (i = 0; i < 4; i++) {
ch = *ptr++;
if (!isxdigit(ch)) {
goto error_inval_hex;
}
low = (low << 4) + hextoi(ch);
}
if (!UTF8LITE_IS_UTF16_LOW(low)) {
ptr -= 6;
goto error_inval_low;
}
} else if (UTF8LITE_IS_UTF16_LOW(code)) {
goto error_inval_nohigh;
}
err = 0;
goto out;
error_inval_incomplete:
err = UTF8LITE_ERROR_INVAL;
utf8lite_message_set(msg, "incomplete escape code (\\u%.*s)",
(int)(end - input), input);
goto out;
error_inval_hex:
err = UTF8LITE_ERROR_INVAL;
utf8lite_message_set(msg, "invalid hex value in escape code (\\u%.*s)",
4, input);
goto out;
error_inval_nolow:
err = UTF8LITE_ERROR_INVAL;
utf8lite_message_set(msg, "missing UTF-16 low surrogate"
" after high surrogate escape code (\\u%.*s)",
4, input);
goto out;
error_inval_low:
err = UTF8LITE_ERROR_INVAL;
utf8lite_message_set(msg, "invalid UTF-16 low surrogate (\\u%.*s)"
" after high surrogate escape code (\\u%.*s)",
4, input, 4, input - 6);
goto out;
error_inval_nohigh:
err = UTF8LITE_ERROR_INVAL;
utf8lite_message_set(msg, "missing UTF-16 high surrogate"
" before low surrogate escape code (\\u%.*s)",
4, input);
goto out;
out:
*bufptr = ptr;
return err;
}int utf8lite_scan_utf8(const uint8_t **bufptr, const uint8_t *end,
struct utf8lite_message *msg)
{
const uint8_t *ptr = *bufptr;
uint_fast8_t ch, ch1;
unsigned nc;
int err;
assert(ptr < end);
/* First byte
* ----------
*
* 1-byte sequence:
* 00: 0000 0000
* 7F: 0111 1111
* (ch1 & 0x80 == 0)
*
* Invalid:
* 80: 1000 0000
* BF: 1011 1111
* C0: 1100 0000
* C1: 1100 0001
* (ch & 0xF0 == 0x80 || ch == 0xC0 || ch == 0xC1)
*
* 2-byte sequence:
* C2: 1100 0010
* DF: 1101 1111
* (ch & 0xE0 == 0xC0 && ch > 0xC1)
*
* 3-byte sequence
* E0: 1110 0000
* EF: 1110 1111
* (ch & 0xF0 == E0)
*
* 4-byte sequence:
* F0: 1111 0000
* F4: 1111 0100
* (ch & 0xFC == 0xF0 || ch == 0xF4)
*/
ch1 = *ptr++;
if ((ch1 & 0x80) == 0) {
goto success;
} else if ((ch1 & 0xC0) == 0x80) {
goto inval_lead;
} else if ((ch1 & 0xE0) == 0xC0) {
if (ch1 == 0xC0 || ch1 == 0xC1) {
goto inval_lead;
}
nc = 1;
} else if ((ch1 & 0xF0) == 0xE0) {
nc = 2;
} else if ((ch1 & 0xFC) == 0xF0 || ch1 == 0xF4) {
nc = 3;
} else {
// expecting bytes in the following ranges: 00..7F C2..F4
goto inval_lead;
}
// ensure string is long enough
if (ptr + nc > end) {
// expecting another continuation byte
goto inval_incomplete;
}
/* First Continuation byte
* -----------
* X + 80..BF:
* 80: 1000 0000
* BF: 1011 1111
* (ch & 0xC0 == 0x80)
*
* E0 + A0..BF:
* A0: 1010 0000
* BF: 1011 1111
* (ch & 0xE0 == 0xA0)
*
* ED + 80..9F:
* 80: 1000 0000
* 9F: 1001 1111
* (ch & 0xE0 == 0x80)
*
* F0 + 90..BF:
* 90: 1001 0000
* BF: 1011 1111
* (ch & 0xF0 == 0x90 || ch & 0xE0 == A0)
*
*/
// validate the first continuation byte
ch = *ptr++;
switch (ch1) {
case 0xE0:
if ((ch & 0xE0) != 0xA0) {
// expecting a byte between A0 and BF
goto inval_cont;
}
break;
case 0xED:
if ((ch & 0xE0) != 0x80) {
// expecting a byte between A0 and 9F
goto inval_cont;
}
break;
case 0xF0:
if ((ch & 0xE0) != 0xA0 && (ch & 0xF0) != 0x90) {
// expecting a byte between 90 and BF
goto inval_cont;
}
break;
case 0xF4:
if ((ch & 0xF0) != 0x80) {
// expecting a byte between 80 and 8F
goto inval_cont;
}
default:
if ((ch & 0xC0) != 0x80) {
// expecting a byte between 80 and BF
goto inval_cont;
}
break;
}
nc--;
// validate the trailing continuation bytes
while (nc-- > 0) {
ch = *ptr++;
if ((ch & 0xC0) != 0x80) {
// expecting a byte between 80 and BF
goto inval_cont;
}
}
success:
err = 0;
goto out;
inval_incomplete:
utf8lite_message_set(msg, "not enough continuation bytes"
" after leading byte (0x%02X)",
(unsigned)ch1);
goto error;
inval_lead:
utf8lite_message_set(msg, "invalid leading byte (0x%02X)",
(unsigned)ch1);
goto error;
inval_cont:
utf8lite_message_set(msg, "leading byte 0x%02X followed by"
" invalid continuation byte (0x%02X)",
(unsigned)ch1, (unsigned)ch);
goto error;
error:
ptr--;
err = UTF8LITE_ERROR_INVAL;
out:
*bufptr = ptr;
return err;
}int utf8lite_text_isascii(const struct utf8lite_text *text)
{
struct utf8lite_text_iter it;
utf8lite_text_iter_make(&it, text);
while (utf8lite_text_iter_advance(&it)) {
if (!UTF8LITE_IS_ASCII(it.current)) {
return 0;
}
}
return 1;
}int utf8lite_text_iter_advance(struct utf8lite_text_iter *it)
{
const uint8_t *ptr = it->ptr;
size_t text_attr = it->text_attr;
int32_t code;
if (it->ptr == it->end) {
goto at_end;
}
code = *ptr++;
if (code == '\\' && (text_attr & UTF8LITE_TEXT_ESC_BIT)) {
utf8lite_decode_escape(&ptr, &code);
} else if (code >= 0x80) {
ptr--;
utf8lite_decode_utf8(&ptr, &code);
}
it->ptr = ptr;
it->current = code;
return 1;
at_end:
it->current = UTF8LITE_CODE_NONE;
return 0;
}void utf8lite_text_iter_make(struct utf8lite_text_iter *it,
const struct utf8lite_text *text)
{
it->ptr = text->ptr;
it->end = it->ptr + UTF8LITE_TEXT_SIZE(text);
it->text_attr = text->attr;
it->current = UTF8LITE_CODE_NONE;
}static int width(const struct utf8lite_text *text)
{
struct utf8lite_graphscan scan;
int width, w;
width = 0;
utf8lite_graphscan_make(&scan, text);
while (utf8lite_graphscan_advance(&scan)) {
ck_assert(!utf8lite_graph_measure(&scan.current,
render.flags, &w));
if (w == -1) {
return w;
}
ck_assert(w >= 0);
ck_assert(width <= INT_MAX - w);
width += w;
}
return width;
}name: CHECK_ERROR_FORMAT_SEP
argument: 'I736 | c_char
function: t(c_int_na, c_int) --> c_int argument: t(c_int_na & 'I111, c_double)
name: RENDER_TAG
SEXP rutf8_alloc_render(int flags)
{
SEXP ans;
struct rutf8_render *obj;
int err = 0;
PROTECT(ans = R_MakeExternalPtr(NULL, RENDER_TAG, R_NilValue));
R_RegisterCFinalizerEx(ans, rutf8_free_render, TRUE);
TRY_ALLOC(obj = calloc(1, sizeof(*obj)));
R_SetExternalPtrAddr(ans, obj);
TRY(utf8lite_render_init(&obj->render, flags));
obj->has_render = 1;
exit:
CHECK_ERROR(err);
UNPROTECT(1);
return ans;
}name: TRY
int text_init(struct utf8lite_text *text, SEXP charsxp)
{
const uint8_t *ptr;
size_t size;
cetype_t ce;
int err = 0;
assert(charsxp != NA_STRING);
ce = getCharCE(charsxp);
if (rutf8_encodes_utf8(ce)) {
ptr = (const uint8_t *)CHAR(charsxp);
size = (size_t)XLENGTH(charsxp);
} else if (ce == CE_LATIN1 || ce == CE_NATIVE) {
ptr = (const uint8_t *)rutf8_translate_utf8(charsxp);
size = strlen((const char *)ptr);
} else {
err = UTF8LITE_ERROR_INVAL; // bytes or other encoding
goto exit;
}
TRY(utf8lite_text_assign(text, ptr, size, 0, NULL));
exit:
return err;
}function: t(c_true, c_true) --> c_true & t(c_false, c_false) | t(c_false, c_true) | t(c_true, c_false) --> c_false
argument: t(c_false, c("internal error: unrecognized charwidth property"))int utf8lite_charwidth(int32_t code)
{
int prop = charwidth(code);
switch(prop) {
case CHARWIDTH_NONE:
return UTF8LITE_CHARWIDTH_NONE;
case CHARWIDTH_IGNORABLE:
return UTF8LITE_CHARWIDTH_IGNORABLE;
case CHARWIDTH_MARK:
return UTF8LITE_CHARWIDTH_MARK;
case CHARWIDTH_NARROW:
return UTF8LITE_CHARWIDTH_NARROW;
case CHARWIDTH_AMBIGUOUS:
return UTF8LITE_CHARWIDTH_AMBIGUOUS;
case CHARWIDTH_WIDE:
return UTF8LITE_CHARWIDTH_WIDE;
case CHARWIDTH_EMOJI:
return UTF8LITE_CHARWIDTH_EMOJI;
default:
assert(0 && "internal error: unrecognized charwidth property");
return prop;
}
}name: UTF8LITE_TEXT_SIZE_MASK
int utf8lite_graphscan_advance(struct utf8lite_graphscan *scan)
{
scan->current.text.ptr = (uint8_t *)scan->ptr;
scan->current.text.attr = (scan->iter.text_attr
& ~UTF8LITE_TEXT_SIZE_MASK);
Start:
// GB2: Break at the end of text
if (scan->prop < 0) {
goto Break;
}
switch ((enum graph_break_prop)scan->prop) {
case GRAPH_BREAK_CR:
NEXT();
goto CR;
case GRAPH_BREAK_CONTROL:
case GRAPH_BREAK_LF:
// Break after controls
// GB4: (Newline | LF) +
NEXT();
goto Break;
case GRAPH_BREAK_L:
NEXT();
goto L;
case GRAPH_BREAK_LV:
case GRAPH_BREAK_V:
NEXT();
goto V;
case GRAPH_BREAK_LVT:
case GRAPH_BREAK_T:
NEXT();
goto T;
case GRAPH_BREAK_PREPEND:
NEXT();
goto Prepend;
case GRAPH_BREAK_EXTENDED_PICTOGRAPHIC:
NEXT();
goto Extended_Pictographic;
case GRAPH_BREAK_REGIONAL_INDICATOR:
NEXT();
goto Regional_Indicator;
case GRAPH_BREAK_EXTEND:
case GRAPH_BREAK_SPACINGMARK:
case GRAPH_BREAK_ZWJ:
case GRAPH_BREAK_OTHER:
NEXT();
goto MaybeBreak;
}
assert(0 && "unhandled grapheme break property");
CR:
// GB3: Do not break within CRLF
// GB4: Otherwise break after controls
if (scan->prop == GRAPH_BREAK_LF) {
NEXT();
}
goto Break;
L:
// GB6: Do not break Hangul syllable sequences.
switch (scan->prop) {
case GRAPH_BREAK_L:
NEXT();
goto L;
case GRAPH_BREAK_V:
case GRAPH_BREAK_LV:
NEXT();
goto V;
case GRAPH_BREAK_LVT:
NEXT();
goto T;
default:
goto MaybeBreak;
}
V:
// GB7: Do not break Hangul syllable sequences.
switch (scan->prop) {
case GRAPH_BREAK_V:
NEXT();
goto V;
case GRAPH_BREAK_T:
NEXT();
goto T;
default:
goto MaybeBreak;
}
T:
// GB8: Do not break Hangul syllable sequences.
switch (scan->prop) {
case GRAPH_BREAK_T:
NEXT();
goto T;
default:
goto MaybeBreak;
}
Prepend:
switch (scan->prop) {
case GRAPH_BREAK_CONTROL:
case GRAPH_BREAK_CR:
case GRAPH_BREAK_LF:
// GB5: break before controls
goto Break;
default:
// GB9b: do not break after Prepend characters.
goto Start;
}
Extended_Pictographic:
// GB9: Do not break before extending characters
while (scan->prop == GRAPH_BREAK_EXTEND) {
NEXT();
}
// GB9: Do not break before ZWJ
if (scan->prop == GRAPH_BREAK_ZWJ) {
NEXT();
// GB11: Do not break within emoji modifier sequences
// or emoji zwj sequences.
if (scan->prop == GRAPH_BREAK_EXTENDED_PICTOGRAPHIC) {
NEXT();
goto Extended_Pictographic;
}
}
goto MaybeBreak;
Regional_Indicator:
// Do not break within emoji flag sequences. That is, do not break
// between regional indicator (RI) symbols if there is an odd number
// of RI characters before the break point
if (scan->prop == GRAPH_BREAK_REGIONAL_INDICATOR) {
// GB12/13: [^RI] RI * RI
NEXT();
}
goto MaybeBreak;
MaybeBreak:
// GB9: Do not break before extending characters or ZWJ.
// GB9a: Do not break before SpacingMark [extended grapheme clusters]
// GB999: Otherwise, break everywhere
switch (scan->prop) {
case GRAPH_BREAK_EXTEND:
case GRAPH_BREAK_SPACINGMARK:
case GRAPH_BREAK_ZWJ:
NEXT();
goto MaybeBreak;
default:
goto Break;
}
Break:
scan->current.text.attr |= (size_t)(scan->ptr - scan->current.text.ptr);
return (scan->ptr == scan->current.text.ptr) ? 0 : 1;
}function: t(*{ length : c_int_na ; string : c_string ; length_max : c_int_na ; flags : c_int_na ; tab : c_string ; tab_length : c_int_na ; newline : c_string ; newline_length : c_int_na ; style_open : c_string ; style_close : c_string ; style_open_length : c_int_na ; style_close_length : c_int_na ; indent : c_int_na ; needs_indent : c_int_na ; error : c_int_na }, c_null) --> empty
argument: t(*('I2312 & { ;; `I27 } & { length : c_int_na ; string : c_string ; length_max : c_int_na ; flags : c_int_na ; tab : c_string ; tab_length : c_int_na ; newline : c_string ; newline_length : c_int_na ; style_open : c_string ; style_close : c_string ; style_open_length : c_int_na ; style_close_length : c_int_na ; indent : c_int_na ; needs_indent : c_int_na ; error : c_int_na }) & 'I2305, *{ ptr : *c_int_na ; attr : c_int_na })int utf8lite_render_string(struct utf8lite_render *r, const char *str)
{
struct utf8lite_text text;
const uint8_t *ptr;
size_t len;
CHECK_ERROR(r);
ptr = (const uint8_t *)str;
len = strlen(str);
r->error = utf8lite_text_assign(&text, ptr, len, 0, NULL);
CHECK_ERROR(r);
return utf8lite_render_text(r, &text);
}name: UTF8LITE_TEXT_SIZE_MAX
int utf8lite_text_assign(struct utf8lite_text *text, const uint8_t *ptr,
size_t size, int flags, struct utf8lite_message *msg)
{
int err = 0;
if (size > UTF8LITE_TEXT_SIZE_MAX) {
err = UTF8LITE_ERROR_OVERFLOW;
utf8lite_message_set(msg, "text size (%"PRIu64" bytes)"
" exceeds maximum (%"PRIu64" bytes)",
(uint64_t)size,
(uint64_t)UTF8LITE_TEXT_SIZE_MAX);
} else if (flags & UTF8LITE_TEXT_UNESCAPE) {
if (flags & UTF8LITE_TEXT_VALID) {
assign_esc_unsafe(text, ptr, size);
} else {
err = assign_esc(text, ptr, size, msg);
}
} else {
if (flags & UTF8LITE_TEXT_VALID) {
assign_raw_unsafe(text, ptr, size);
} else {
err = assign_raw(text, ptr, size, msg);
}
}
if (err) {
text->ptr = NULL;
text->attr = 0;
}
return err;
}name: UTF8LITE_TEXT_SIZE_MASK
void utf8lite_text_iter_reset(struct utf8lite_text_iter *it)
{
const size_t size = (it->text_attr & UTF8LITE_TEXT_SIZE_MASK);
const uint8_t *begin = it->end - size;
it->ptr = begin;
it->current = UTF8LITE_CODE_NONE;
}static void rutf8_bytes_lrender(struct utf8lite_render *r,
const struct rutf8_bytes *bytes,
int width_min, int quote, int centre)
{
const uint8_t *ptr, *end;
uint8_t byte;
int err = 0, w, fullwidth, width, quotes;
assert(width_min >= 0);
quotes = quote ? 2 : 0;
width = 0;
if (centre && width_min > 0) {
fullwidth = rutf8_bytes_width(bytes, r->flags);
if (fullwidth <= width_min - quotes) {
width = (width_min - fullwidth - quotes) / 2;
TRY(utf8lite_render_chars(r, ' ', width));
}
}
if (quote) {
TRY(utf8lite_render_raw(r, "\"", 1));
assert(width < INT_MAX);
width++;
}
ptr = bytes->ptr;
end = bytes->ptr + bytes->size;
while (ptr < end) {
byte = *ptr++;
w = byte_width(byte, r->flags);
render_byte(r, byte);
assert(w >= 0);
if (width <= width_min - w) {
width += w;
} else {
width = width_min; // truncate to avoid overflow
}
}
if (quote) {
TRY(utf8lite_render_raw(r, "\"", 1));
if (width < width_min) { // avoid overflow
width++;
}
}
if (width < width_min) {
TRY(utf8lite_render_chars(r, ' ', width_min - width));
}
exit:
CHECK_ERROR(err);
}SEXP rutf8_utf8_encode(SEXP sx, SEXP swidth, SEXP squote, SEXP sjustify,
SEXP sescapes, SEXP sdisplay, SEXP sutf8)
{
SEXP ans, selt, ans_i = NA_STRING, srender;
struct rutf8_string elt;
struct utf8lite_render *render;
enum rutf8_justify_type justify;
const char *escapes;
R_xlen_t i, n;
int width, quote, display, utf8;
int err = 0, nprot = 0, w, quotes, flags;
if (sx == R_NilValue) {
return R_NilValue;
}
if (!isString(sx)) {
Rf_error("argument is not a character object");
}
n = XLENGTH(sx);
if (swidth == R_NilValue || INTEGER(swidth)[0] == NA_INTEGER) {
width = -1;
} else {
width = INTEGER(swidth)[0];
}
quote = LOGICAL(squote)[0] == TRUE;
justify = rutf8_as_justify(sjustify);
escapes = rutf8_as_style(sescapes);
display = LOGICAL(sdisplay)[0] == TRUE;
utf8 = LOGICAL(sutf8)[0] == TRUE;
flags = (UTF8LITE_ESCAPE_CONTROL | UTF8LITE_ENCODE_C);
if (quote) {
flags |= UTF8LITE_ESCAPE_DQUOTE;
}
if (display) {
flags |= UTF8LITE_ENCODE_RMDI;
flags |= UTF8LITE_ENCODE_EMOJIZWSP;
}
if (!utf8) {
flags |= UTF8LITE_ESCAPE_UTF8;
}
#if defined(_WIN32) || defined(_WIN64)
flags |= UTF8LITE_ESCAPE_EXTENDED;
#endif
quotes = quote ? 2 : 0;
if (justify == RUTF8_JUSTIFY_NONE) {
width = 0;
}
if (width < 0) {
width = 0;
for (i = 0; i < n; i++) {
CHECK_INTERRUPT(i);
PROTECT(selt = STRING_ELT(sx, i)); nprot++;
rutf8_string_init(&elt, selt);
if (elt.type == RUTF8_STRING_NONE) {
UNPROTECT(1); nprot--;
continue;
}
w = rutf8_string_width(&elt, flags);
if (w > INT_MAX - quotes) {
Rf_error("width exceeds maximum (%d)",
INT_MAX);
}
w += quotes;
if (w > width) {
width = w;
}
UNPROTECT(1); nprot--;
}
}
PROTECT(srender = rutf8_alloc_render(flags)); nprot++;
render = rutf8_as_render(srender);
if (escapes) {
TRY(utf8lite_render_set_style(render, escapes,
RUTF8_STYLE_CLOSE));
}
PROTECT(ans = duplicate(sx)); nprot++;
for (i = 0; i < n; i++) {
CHECK_INTERRUPT(i);
PROTECT(selt = STRING_ELT(sx, i)); nprot++;
rutf8_string_init(&elt, selt);
if (elt.type == RUTF8_STRING_NONE) {
ans_i = NA_STRING;
} else {
rutf8_string_render(render, &elt, width, quote,
justify);
ans_i = mkCharLenCE(render->string, render->length,
CE_UTF8);
utf8lite_render_clear(render);
}
UNPROTECT(1); nprot--;
SET_STRING_ELT(ans, i, ans_i);
}
exit:
UNPROTECT(nprot);
CHECK_ERROR(err);
return ans;
}void utf8lite_decode_utf8(const uint8_t **bufptr, int32_t *codeptr)
{
const uint8_t *ptr = *bufptr;
int32_t code;
uint_fast8_t ch;
unsigned nc;
ch = *ptr++;
if (!(ch & 0x80)) {
code = ch;
nc = 0;
} else if (!(ch & 0x20)) {
code = ch & 0x1F;
nc = 1;
} else if (!(ch & 0x10)) {
code = ch & 0x0F;
nc = 2;
} else {
code = ch & 0x07;
nc = 3;
}
while (nc-- > 0) {
ch = *ptr++;
code = (code << 6) + (ch & 0x3F);
}
*bufptr = ptr;
*codeptr = code;
}void utf8lite_render_clear(struct utf8lite_render *r)
{
r->string[0] = '\0';
r->length = 0;
r->indent = 0;
r->needs_indent = 1;
r->error = 0;
}int utf8lite_render_init(struct utf8lite_render *r, int flags)
{
int err;
r->string = malloc(1);
if (!r->string) {
err = UTF8LITE_ERROR_NOMEM;
return err;
}
r->length = 0;
r->length_max = 0;
r->flags = flags;
r->tab = "\t";
r->tab_length = (int)strlen(r->tab);
r->newline = "\n";
r->newline_length = (int)strlen(r->newline);
r->style_open = NULL;
r->style_open_length = 0;
r->style_close = NULL;
r->style_close_length = 0;
utf8lite_render_clear(r);
return 0;
}int utf8lite_render_set_style(struct utf8lite_render *r,
const char *open, const char *close)
{
size_t open_len = 0, close_len = 0;
CHECK_ERROR(r);
if (open) {
if ((open_len = strlen(open)) >= INT_MAX) {
r->error = UTF8LITE_ERROR_OVERFLOW;
return r->error;
}
}
if (close) {
if ((close_len = strlen(close)) >= INT_MAX) {
r->error = UTF8LITE_ERROR_OVERFLOW;
return r->error;
}
}
r->style_open = open;
r->style_close = close;
r->style_open_length = (int)open_len;
r->style_close_length = (int)close_len;
return 0;
}