Implement ascii_downcase and ascii_upcase in C (#3689)
Both were defined as explode | map(...) | implode, which took 4 seconds on a 10 MB string, about 90 times as long as parsing it. Change the case of A-Z or a-z a byte at a time in C, copying multibyte UTF-8 sequences unchanged, so the output stays the same and the 10 MB case now takes about 60 ms including parsing. On a non-string input the error now names the function, "ascii_downcase input must be a string", instead of "explode input must be a string".
This commit is contained in:
1 parent
bd56a74562
commit
864d769d88
3 files changed
+50
-6
No files matched your search
@@ -1355,6 +1355,36 @@ static jv f_string_explode(jq_state *jq, jv a) {
|
||||
return jv_string_explode(a);
|
||||
}
|
||||
|
||||
// Only the letters from..to change case. Bytes of multibyte UTF-8 sequences
|
||||
// are all >= 0x80, so they are copied unchanged.
|
||||
static void ascii_case_copy(char *dst, const char *src, int len, char from,
|
||||
char to) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
char c = src[i];
|
||||
dst[i] = (c >= from && c <= to) ? c ^ 0x20 : c; // flip case: 'A' <-> 'a'
|
||||
}
|
||||
}
|
||||
|
||||
static jv ascii_case(jv input, char from, char to, const char *errmsg) {
|
||||
if (jv_get_kind(input) != JV_KIND_STRING)
|
||||
return ret_error(input, jv_string(errmsg));
|
||||
int len = jv_string_length_bytes(jv_copy(input));
|
||||
char *result = jv_mem_alloc((size_t)len + 1);
|
||||
ascii_case_copy(result, jv_string_value(input), len, from, to);
|
||||
jv ret = jv_string_sized(result, len);
|
||||
free(result);
|
||||
jv_free(input);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static jv f_ascii_downcase(jq_state *jq, jv input) {
|
||||
return ascii_case(input, 'A', 'Z', "ascii_downcase input must be a string");
|
||||
}
|
||||
|
||||
static jv f_ascii_upcase(jq_state *jq, jv input) {
|
||||
return ascii_case(input, 'a', 'z', "ascii_upcase input must be a string");
|
||||
}
|
||||
|
||||
static jv f_string_indexes(jq_state *jq, jv a, jv b) {
|
||||
if (jv_get_kind(a) != JV_KIND_STRING) {
|
||||
jv_free(b);
|
||||
@@ -2015,6 +2045,8 @@ BINOPS
|
||||
CFUNC(f_string_split, "split", 2),
|
||||
CFUNC(f_string_explode, "explode", 1),
|
||||
CFUNC(f_string_implode, "implode", 1),
|
||||
CFUNC(f_ascii_downcase, "ascii_downcase", 1),
|
||||
CFUNC(f_ascii_upcase, "ascii_upcase", 1),
|
||||
CFUNC(f_string_indexes, "_strindices", 2),
|
||||
CFUNC(f_string_trim, "trim", 1),
|
||||
CFUNC(f_string_ltrim, "ltrim", 1),
|
||||
|
||||
@@ -184,12 +184,6 @@ def repeat(exp):
|
||||
exp, _repeat;
|
||||
_repeat;
|
||||
def inputs: try repeat(input) catch if .=="break" then empty else error end;
|
||||
# like ruby's downcase - only characters A to Z are affected
|
||||
def ascii_downcase:
|
||||
explode | map( if 65 <= . and . <= 90 then . + 32 else . end) | implode;
|
||||
# like ruby's upcase - only characters a to z are affected
|
||||
def ascii_upcase:
|
||||
explode | map( if 97 <= . and . <= 122 then . - 32 else . end) | implode;
|
||||
|
||||
# Streaming utilities
|
||||
def truncate_stream(stream):
|
||||
|
||||
@@ -1888,6 +1888,24 @@ ascii_upcase
|
||||
"useful but not for é"
|
||||
"USEFUL BUT NOT FOR é"
|
||||
|
||||
# Only A-Z and a-z change; the neighbouring bytes @ [ ` { and non-ASCII do not
|
||||
[ascii_downcase, ascii_upcase]
|
||||
"@AZ[`az{ ÀÉé Dž 日本 😀 0123456789 THE QUICK brown fox JUMPS over"
|
||||
["@az[`az{ ÀÉé Dž 日本 😀 0123456789 the quick brown fox jumps over","@AZ[`AZ{ ÀÉé Dž 日本 😀 0123456789 THE QUICK BROWN FOX JUMPS OVER"]
|
||||
|
||||
[ascii_downcase, ascii_upcase]
|
||||
""
|
||||
["",""]
|
||||
|
||||
# An embedded NUL does not end the string
|
||||
[ascii_downcase, ascii_upcase]
|
||||
"A\u0000z"
|
||||
["a\u0000z","A\u0000Z"]
|
||||
|
||||
[.[] | try ascii_downcase catch ., try ascii_upcase catch .]
|
||||
[null, ["A"]]
|
||||
["ascii_downcase input must be a string","ascii_upcase input must be a string","ascii_downcase input must be a string","ascii_upcase input must be a string"]
|
||||
|
||||
bsearch(0,1,2,3,4)
|
||||
[1,2,3]
|
||||
-1
|
||||
|
||||
Reference in new issue
Block a user