static ss_t *aux_resize(ss_t **s, const sbool_t cat, const ss_t *src, const size_t n, char fill_byte) { ASSERT_RETURN_IF(!s, ss_void); if (!src) src = ss_void; const size_t src_size = sd_get_size(src), at = (cat && *s) ? sd_get_size(*s) : 0, out_size = at + n; /* BEHAVIOR: n overflow TODO */ const sbool_t aliasing = *s == src; if (src_size < n) { /* fill */ if (ss_reserve(s, out_size) >= out_size) { char *o = get_str(*s); if (!aliasing) { const char *p = get_str_r(src); memcpy(o + at, p, src_size); } memset(o + at + src_size, fill_byte, n - src_size); set_size(*s, out_size); } } else { /* else: cut (implicit) */ if (ss_reserve(s, out_size) >= out_size) { if (!aliasing) memcpy(get_str(*s) + at, get_str_r(src), n); set_size(*s, out_size); } } return *s; }
static ss_t *aux_resize_u(ss_t **s, const sbool_t cat, ss_t *src, const size_t u_chars, int fill_char) { ASSERT_RETURN_IF(!s, ss_void); if (!src) src = ss_void; const size_t at = (cat && *s) ? sd_get_size(*s) : 0, char_size = sc_wc_to_utf8_size(fill_char), current_u_chars = ss_len_u(src); RETURN_IF(u_chars == current_u_chars, ss_check(s)); /* same */ const sbool_t aliasing = *s == src; const size_t srcs = sd_get_size(src); if (current_u_chars < u_chars) { /* fill */ const size_t new_elems = u_chars - current_u_chars, at_inc = srcs + new_elems * char_size; SS_OVERFLOW_CHECK(s, at, at_inc); const size_t out_size = at + at_inc; if (ss_reserve(s, out_size) >= out_size) { if (!cat && !aliasing) /* copy */ ss_clear(s); if (!aliasing) { memcpy(get_str(*s) + at, get_str_r(src), srcs); inc_unicode_size(*s, current_u_chars); inc_size(*s, srcs); } size_t i = 0; for (; i < new_elems; i++) ss_cat_char(s, fill_char); } } else { /* cut */ const char *ps = get_str_r(src); size_t actual_unicode_count = 0; const size_t head_size = sc_unicode_count_to_utf8_size( ps, 0, srcs, u_chars, &actual_unicode_count); SS_OVERFLOW_CHECK(s, at, head_size); const size_t out_size = at + head_size; S_ASSERT(u_chars == actual_unicode_count); if (!aliasing) { /* copy or cat */ if (ss_reserve(s, out_size) >= out_size) { if (!cat && !aliasing) /* copy */ ss_clear(s); memcpy(get_str(*s) + at, ps, head_size); inc_unicode_size(*s, actual_unicode_count); inc_size(*s, head_size); } } else { /* cut */ set_size(*s, head_size); set_unicode_size(*s, actual_unicode_count); } } return *s; }
sbool_t aux_jpeg_dec_init(struct jpeg_decompress_struct *jd, struct aux_jpeg *ja, struct RGB_Info *ri, const ss_t *jpg_in, ss_t **rgb_out) { RETURN_IF(!ja || !ri || !jpg_in || !rgb_out, S_FALSE); jpeg_create_decompress(jd); jd->err->trace_level = 0; jd->err->error_exit = jpg_error_exit; ja->in = jpg_in; ja->out = rgb_out; jd->src = &ja->jsrc; aux_jpeg_common_init(ja); ja->jsrc.init_source = aux_jpegd_init; ja->jsrc.fill_input_buffer = aux_jpegd_fill_input_buffer; ja->jsrc.skip_input_data = aux_jpegd_skip_input_data; ja->jsrc.resync_to_restart = jpeg_resync_to_restart; ja->jsrc.term_source = aux_jpegd_term_source; ja->jsrc.next_input_byte = (JOCTET *)ss_get_buffer_r(jpg_in); ja->jsrc.bytes_in_buffer = ss_size(jpg_in); RETURN_IF(jpeg_read_header(jd, TRUE) != JPEG_HEADER_OK, 0); jpeg_start_decompress(jd); set_rgbi(ri, jd->output_width, jd->output_height, jd->out_color_components * 8, jd->out_color_components); RETURN_IF(!valid_rgbi(ri), S_FALSE); size_t rgb_size = ri->row_size * ri->height; if (ss_reserve(rgb_out, rgb_size) >= rgb_size) return S_TRUE; jpeg_destroy_decompress(jd); return S_FALSE; }
static ss_t *aux_rtrim(ss_t **s, const sbool_t cat, const ss_t *src) { ASSERT_RETURN_IF(!s, ss_void); if (!src) src = ss_void; const size_t ss = sd_get_size(src), at = (cat && *s) ? sd_get_size(*s) : 0; if (ss > 0) { const sbool_t aliasing = *s == src; const char *ps = get_str_r(src); size_t i = ss - 1; for (; i > 0 && isspace(ps[i]); i--); if (isspace(ps[i])) i--; const size_t nspaces = ss - i - 1, copy_size = ss - nspaces, out_size = at + copy_size, cat_usize = cat ? get_unicode_size(*s) : 0, src_usize = *s ? get_unicode_size(*s) : 0; if (ss_reserve(s, out_size) >= out_size) { char *pt = get_str(*s); if (!aliasing) memcpy(pt + at, ps, copy_size); set_size(*s, out_size); set_unicode_size(*s, cat_usize + src_usize - nspaces); } } else { if (cat) ss_check(s); else ss_clear(s); } return *s; }
static size_t tga2rgb(ss_t **rgb, struct RGB_Info *ri, const ss_t *tga) { const char *t = ss_get_buffer_r(tga); const size_t ts = ss_size(tga); RETURN_IF(ts < TGA_RGBHDR || !valid_tga(t), 0); set_rgbi(ri, S_LD_LE_U16(t + TGA_W), S_LD_LE_U16(t + TGA_H), t[TGA_BPP], t[TGA_TYPE] == TGA_RAW_GRAY ? 1 : t[TGA_BPP]/8); RETURN_IF(!valid_rgbi(ri), 0); size_t rgb_bytes = ri->row_size * ri->height; RETURN_IF(ts < rgb_bytes + TGA_RGBHDR, 0); RETURN_IF(ss_reserve(rgb, rgb_bytes) < rgb_bytes, 0); if ((t[TGA_DESC] & TGA_TOP_LEFT) != 0) { if (ri->chn == 1) ss_cpy_cn(rgb, t + TGA_RGBHDR, rgb_bytes); else tga_rgb_swap(ri->bpp, rgb_bytes, ss_get_buffer_r(tga) + TGA_RGBHDR, ss_get_buffer(*rgb)); } else { ss_set_size(*rgb, 0); ssize_t i = (ssize_t)(ri->height - 1) * ri->row_size; for (; i >= 0; i -= ri->row_size) if (ri->chn == 1) ss_cat_cn(rgb, t + TGA_RGBHDR + i, ri->row_size); else tga_rgb_swap(ri->bpp, ri->row_size, t + TGA_RGBHDR + i, ss_get_buffer(*rgb) + i); } ss_set_size(*rgb, rgb_bytes); return rgb_bytes; }
static ss_t *aux_erase(ss_t **s, const sbool_t cat, const ss_t *src, const size_t off, const size_t n) { ASSERT_RETURN_IF(!s, ss_void); if (!src) src = ss_void; const size_t ss0 = sd_get_size(src), at = (cat && *s) ? sd_get_size(*s) : 0; const sbool_t overflow = off + n > ss0; const size_t src_size = overflow ? ss0 - off : n, copy_size = ss0 - off - src_size; if (*s == src) { /* BEHAVIOR: aliasing: copy-only */ if (off + n >= ss0) { /* tail clean cut */ set_size(*s, off); } else { char *ps = get_str(*s); memmove(ps + off, ps + off + n, copy_size); set_size(*s, ss0 - n); } set_unicode_size_cached(*s, S_FALSE); } else { /* copy or cat */ const size_t out_size = at + off + copy_size; if (ss_reserve(s, out_size) >= out_size) { char *po = get_str(*s); memcpy(po + at, get_str_r(src), off); memcpy(po + at + off, get_str_r(src) + off + n, copy_size); set_size(*s, out_size); set_unicode_size_cached(*s, S_FALSE); } } return ss_check(s); }
static ss_t *aux_toenc(ss_t **s, const sbool_t cat, const ss_t *src, senc_f_t f) { ASSERT_RETURN_IF(!s, ss_void); if (!src) src = ss_void; const int aliasing = *s == src; const size_t in_size = sd_get_size(src), at = (cat && *s) ? sd_get_size(*s) : 0, out_size = at + f(NULL, in_size, NULL); if (ss_reserve(s, out_size) >= out_size) { const ss_t *src1 = aliasing ? *s : src; f((const unsigned char *)get_str_r(src1), in_size, (unsigned char *)get_str(*s) + at); if (at == 0) { set_unicode_size_cached(*s, S_TRUE); set_unicode_size(*s, in_size * 2); } else { /* cat */ if (is_unicode_size_cached(*s) && at == sd_get_size(*s)) set_unicode_size(*s, get_unicode_size(*s) + in_size * 2); else set_unicode_size_cached(*s, S_FALSE); } set_size(*s, out_size); } return ss_check(s); }
static sbool_t aux_png_read_set_rows(png_bytep *rows, const png_info *pi, ss_t **rgb) { RETURN_IF(!rows || !pi || !rgb, S_FALSE); size_t rs = aux_png_row_size(pi), rgb_size = rs * pi->height; RETURN_IF(ss_reserve(rgb, rgb_size) < rgb_size, S_FALSE); ss_set_size(*rgb, rgb_size); return aux_png_set_rows(rows, pi, ss_get_buffer(*rgb)); }
ss_t *ss_cpy_char(ss_t **s, const int c) { ASSERT_RETURN_IF(!s, ss_void); if (ss_reserve(s, SSU8_MAX_SIZE) >= SSU8_MAX_SIZE) { ss_clear(s); return ss_cat_char(s, c); } return *s; }
ss_t *ss_cpy_printf_va(ss_t **s, const size_t size, const char *fmt, va_list ap) { ASSERT_RETURN_IF(!s, ss_void); ASSERT_RETURN_IF((!size || !fmt) && ss_clear(s), *s); if (*s) { ss_reserve(s, size); ss_clear(s); } return ss_cat_printf_va(s, size, fmt, ap); }
sbool_t aux_jpeg_enc_init(struct jpeg_compress_struct *jc, struct aux_jpeg *ja, const struct RGB_Info *ri, const ss_t *rgb, ss_t **jpg_out) { RETURN_IF(!ja || !valid_rgbi(ri) || !rgb || !jpg_out, S_FALSE); ss_set_size(*jpg_out, 0); jpeg_create_compress(jc); jc->in_color_space = ri->chn == 1 ? JCS_GRAYSCALE : JCS_RGB; jpeg_set_defaults(jc); jc->input_components = ri->chn; jc->data_precision = ri->bpp / ri->chn; jc->image_width = ri->width; jc->image_height = ri->height; jpeg_default_colorspace(jc); jc->smoothing_factor = JPG_SMOOTH; jc->arith_code = FALSE; if (ri->chn == 1) jpeg_set_colorspace(jc, JCS_GRAYSCALE); jc->mem->max_memory_to_use = JPG_TMP_MAX_MEM; jc->restart_in_rows = 0; jc->optimize_coding = TRUE; jc->err->trace_level = 0; jc->err->error_exit = jpg_error_exit; #ifdef S_HW_FPU jc->dct_method = JDCT_FLOAT; #else jc->dct_method = JDCT_ISLOW; #endif #ifdef JPG_SUBS_HQ /* No chroma subsampling (better quality, +30% size) */ jc->comp_info[0].h_samp_factor = jc->comp_info[0].v_samp_factor = jc->comp_info[1].h_samp_factor = jc->comp_info[1].v_samp_factor = jc->comp_info[2].h_samp_factor = jc->comp_info[2].v_samp_factor = 1; #endif jpeg_set_quality(jc, JPG_QUALITY, TRUE); aux_jpeg_common_init(ja); /* Max out size: this is an heuristic (adjusted afterwards) */ ja->max_out_size = (ri->width * ri->height * ri->bpp) / 8; RETURN_IF(ss_reserve(jpg_out, ja->max_out_size) < ja->max_out_size, S_FALSE); ja->in = rgb; ja->out = jpg_out; ja->ri_enc = ri; ja->errors = S_FALSE; ja->jdest.init_destination = aux_jpege_init; ja->jdest.empty_output_buffer = aux_jpege_empty_output_buffer; ja->jdest.term_destination = aux_jpege_term_destination; jc->client_data = ja; jc->dest = &ja->jdest; return S_TRUE; }
ss_t *ss_cpy_printf(ss_t **s, const size_t size, const char *fmt, ...) { ASSERT_RETURN_IF(!s, ss_void); ASSERT_RETURN_IF((!size || !fmt) && ss_clear(s), *s); if (*s) { ss_reserve(s, size); ss_clear(s); } va_list ap; va_start(ap, fmt); ss_cat_printf_va(s, size, fmt, ap); va_end(ap); return *s; }
static ss_t *aux_erase_u(ss_t **s, const sbool_t cat, const ss_t *src, const size_t char_off, const size_t n) { ASSERT_RETURN_IF(!s, ss_void); if (!src) src = ss_void; const char *ps = get_str_r(src); const size_t sso0 = *s ? sd_get_size(*s) : 0, ss0 = sd_get_size(src), head_size = sc_unicode_count_to_utf8_size(ps, 0, ss0, char_off, NULL); RETURN_IF(head_size >= ss0, ss_check(s)); /* BEHAVIOR */ size_t actual_n = 0; const size_t cus = *s ? get_unicode_size(*s) : 0, cut_size = sc_unicode_count_to_utf8_size(ps, head_size, ss0, n, &actual_n), tail_size = ss0 - cut_size - head_size; size_t out_size = ss0 - cut_size, prefix_usize = 0; if (*s == src) { /* aliasing: copy-only */ char *po = get_str(*s); memmove(po + head_size, ps + head_size + cut_size, tail_size); } else { /* copy/cat */ const size_t at = (cat && *s) ? sd_get_size(*s) : 0; out_size += at; if (ss_reserve(s, out_size) >= out_size) { char *po = get_str(*s); memcpy(po + at, get_str_r(src), head_size); memcpy(po + at + head_size, get_str_r(src) + head_size + cut_size, tail_size); set_size(*s, out_size); if (is_unicode_size_cached(*s) && (at == 0 || at == sso0)) { prefix_usize = get_unicode_size(*s); } else { set_unicode_size_cached(*s, S_FALSE); } } } if (cus > actual_n) set_unicode_size(*s, prefix_usize + cus - actual_n); else /* BEHAVIOR: unicode char count invalidation */ set_unicode_size_cached(*s, S_FALSE); set_size(*s, out_size); return ss_check(s); }
static ss_t *aux_ltrim(ss_t **s, const sbool_t cat, const ss_t *src) { ASSERT_RETURN_IF(!s, ss_void); if (!src) src = ss_void; const size_t ss = sd_get_size(src); if (ss > 0) { const sbool_t aliasing = *s == src; const char *ps = get_str_r(src); size_t i = 0; for (; i < ss && isspace(ps[i]); i++); size_t at, cat_usize; if (cat) { if (*s) { at = sd_get_size(*s); cat_usize = get_unicode_size(*s); } else { at = cat_usize = 0; } } else { at = cat_usize = 0; } const size_t out_size = at + ss - i, src_usize = get_unicode_size(src); if (ss_reserve(s, out_size) >= out_size) { char *pt = get_str(*s); if (!aliasing) /* copy or cat: shift data */ memcpy(pt + at, ps + i, ss - i); else if (i > 0) /* copy: shift data */ memmove(pt, ps + i, ss - i); set_size(*s, at + ss - i); set_unicode_size(*s, cat_usize + src_usize - i); } } else { if (cat) ss_check(s); else ss_clear(s); } return *s; }
static ss_t *aux_toint(ss_t **s, const sbool_t cat, const sint_t num) { ASSERT_RETURN_IF(!s, ss_void); char btmp[128], *p = btmp + sizeof(btmp) - 1; sint_t n = num < 0 ? -num : num; do { *p-- = '0' + n % 10; n /= 10; } while (n); if (num < 0) *p-- = '-'; const size_t off = (size_t)((p - (char *)btmp) + 1), digits = sizeof(btmp) - off, at = (cat && *s) ? sd_get_size(*s) : 0; SS_OVERFLOW_CHECK(s, at, digits); const size_t out_size = at + digits; if (ss_reserve(s, out_size) >= out_size) { memcpy(get_str(*s) + at, btmp + off, digits); set_size(*s, out_size); inc_unicode_size(*s, digits); } return *s; }
static size_t rgb2tga(ss_t **tga, const ss_t *rgb, const struct RGB_Info *ri) { RETURN_IF(!rgb || (!valid_rgbi(ri) && (ri->bpp / ri->chn) != 8), 0); RETURN_IF(ri->chn != 1 && ri->chn != 3 && ri->chn != 4, 0); size_t buf_size = ri->bmp_size + TGA_RGBHDR; RETURN_IF(ss_reserve(tga, buf_size) < buf_size, 0); char *h = ss_get_buffer(*tga); memset(h, 0, TGA_RGBHDR); h[TGA_ID] = TGA_NO_X_INFO; h[TGA_CMAP] = TGA_NO_CMAP; h[TGA_TYPE] = ri->chn == 1 ? TGA_RAW_GRAY : TGA_RAW_RGB; S_ST_LE_U16(h + TGA_W, ri->width); S_ST_LE_U16(h + TGA_H, ri->height); h[TGA_BPP] = ri->bpp; h[TGA_DESC] = TGA_TOP_LEFT; ss_cpy_cn(tga, h, TGA_RGBHDR); if (ri->chn == 1) ss_cat(tga, rgb); else tga_rgb_swap(ri->bpp, ri->bmp_size, ss_get_buffer_r(rgb), ss_get_buffer(*tga) + TGA_RGBHDR); ss_set_size(*tga, buf_size); return ss_size(*tga); }
static ss_t *aux_replace(ss_t **s, const sbool_t cat, const ss_t *src, const size_t off, const ss_t *s1, const ss_t *s2) { ASSERT_RETURN_IF(!s, ss_void); if (!s1) s1 = ss_void; if (!s2) s2 = ss_void; if (!src) src = ss_void; const size_t at = (cat && *s) ? sd_get_size(*s) : 0; const char *p0 = get_str_r(src), *p2 = get_str_r(s2); const size_t l1 = sd_get_size(s1), l2 = sd_get_size(s2); size_t i = off, l = sd_get_size(src); ss_t *out = NULL; ssize_t size_delta = l2 > l1 ? (ssize_t)(l2 - l1) : -(ssize_t)(l1 - l2); sbool_t aliasing = S_FALSE; size_t out_size = at + l; char *o, *o0; if (l2 >= l1) { /* resize required */ size_t nfound = 0; /* scan required size */ for (;; i+= l1, nfound++) if ((i = ss_find(src, i, s1)) == S_NPOS) break; if (nfound == 0) /* 0 occurrences: return */ return ss_check(s); if (size_delta >= 0) out_size += (size_t)size_delta * nfound; else out_size -= (size_t)(-size_delta) * nfound; /* allocate output string */ out = ss_alloc(out_size); if (!out) { S_ERROR("not enough memory"); sd_set_alloc_errors(*s); return ss_check(s); } o0 = o = get_str(out); /* copy prefix data (cat) */ if (at > 0) memcpy(o, get_str_r(*s), at); } else { if (s && *s && *s == src) { aliasing = S_TRUE; } else { if (ss_reserve(s, out_size) < out_size) /* BEHAVIOR */ return ss_check(s); } o0 = o = get_str(*s); } typedef void (*memcpy_t)(void *, const void *, size_t); memcpy_t f_cpy; if (aliasing) { f_cpy = (memcpy_t)memmove; } else { f_cpy = (memcpy_t)memcpy; o += at; if (off > 0) /* copy not affected data */ memcpy(o, p0, off); } o += off; size_t i_next = s1 == s2? S_NPOS : /* no replace */ ss_find(src, i + off, s1); for (i = off;;) { /* before match copy: */ if (i_next == S_NPOS) { f_cpy(o, p0 + i, l - i); o += (l - i); break; } f_cpy(o, p0 + i, i_next - i); o += (i_next - i); /* replace: */ f_cpy(o, p2, l2); o += l2; i = i_next + l1; /* prepare next search: */ i_next = ss_find(src, i, s1); } if (out) { ss_t *s_bck = *s; *s = out; ss_free(&s_bck); } set_size(*s, (size_t)(o - o0)); return *s; }