/* vi:set ts=8 sts=4 sw=4 noet: * * VIM - Vi IMproved by Bram Moolenaar * * Do ":help uganda" in Vim to read copying and usage conditions. * Do ":help credits" in Vim to see a list of people who contributed. * See README.txt for an overview of the Vim source code. */ /* * blob.c: Blob support by Yasuhiro Matsumoto */ #include "vim.h" #if defined(FEAT_EVAL) || defined(PROTO) /* * Allocate an empty blob. * Caller should take care of the reference count. */ blob_T * blob_alloc(void) { blob_T *blob = ALLOC_CLEAR_ONE(blob_T); if (blob != NULL) ga_init2(&blob->bv_ga, 1, 100); return blob; } /* * Allocate an empty blob for a return value, with reference count set. * Returns OK or FAIL. */ int rettv_blob_alloc(typval_T *rettv) { blob_T *b = blob_alloc(); if (b == NULL) return FAIL; rettv_blob_set(rettv, b); return OK; } /* * Set a blob as the return value. */ void rettv_blob_set(typval_T *rettv, blob_T *b) { rettv->v_type = VAR_BLOB; rettv->vval.v_blob = b; if (b != NULL) ++b->bv_refcount; } int blob_copy(blob_T *from, typval_T *to) { int ret = OK; to->v_type = VAR_BLOB; to->v_lock = 0; if (from == NULL) to->vval.v_blob = NULL; else if (rettv_blob_alloc(to) == FAIL) ret = FAIL; else { int len = from->bv_ga.ga_len; if (len > 0) { to->vval.v_blob->bv_ga.ga_data = vim_memsave(from->bv_ga.ga_data, len); if (to->vval.v_blob->bv_ga.ga_data == NULL) len = 0; } to->vval.v_blob->bv_ga.ga_len = len; to->vval.v_blob->bv_ga.ga_maxlen = len; } return ret; } void blob_free(blob_T *b) { ga_clear(&b->bv_ga); vim_free(b); } /* * Unreference a blob: decrement the reference count and free it when it * becomes zero. */ void blob_unref(blob_T *b) { if (b != NULL && --b->bv_refcount <= 0) blob_free(b); } /* * Get the length of data. */ long blob_len(blob_T *b) { if (b == NULL) return 0L; return b->bv_ga.ga_len; } /* * Get byte "idx" in blob "b". * Caller must check that "idx" is valid. */ int blob_get(blob_T *b, int idx) { return ((char_u*)b->bv_ga.ga_data)[idx]; } /* * Store one byte "byte" in blob "blob" at "idx". * Caller must make sure that "idx" is valid. */ void blob_set(blob_T *blob, int idx, int byte) { ((char_u*)blob->bv_ga.ga_data)[idx] = byte; } /* * Store one byte "byte" in blob "blob" at "idx". * Append one byte if needed. */ void blob_set_append(blob_T *blob, int idx, int byte) { garray_T *gap = &blob->bv_ga; // Allow for appending a byte. Setting a byte beyond // the end is an error otherwise. if (idx < gap->ga_len || (idx == gap->ga_len && ga_grow(gap, 1) == OK)) { blob_set(blob, idx, byte); if (idx == gap->ga_len) ++gap->ga_len; } } /* * Return TRUE when two blobs have exactly the same values. */ int blob_equal( blob_T *b1, blob_T *b2) { int i; int len1 = blob_len(b1); int len2 = blob_len(b2); // empty and NULL are considered the same if (len1 == 0 && len2 == 0) return TRUE; if (b1 == b2) return TRUE; if (len1 != len2) return FALSE; for (i = 0; i < b1->bv_ga.ga_len; i++) if (blob_get(b1, i) != blob_get(b2, i)) return FALSE; return TRUE; } /* * Read "blob" from file "fd". * Return OK or FAIL. */ int read_blob(FILE *fd, blob_T *blob) { struct stat st; if (fstat(fileno(fd), &st) < 0) return FAIL; if (ga_grow(&blob->bv_ga, st.st_size) == FAIL) return FAIL; blob->bv_ga.ga_len = st.st_size; if (fread(blob->bv_ga.ga_data, 1, blob->bv_ga.ga_len, fd) < (size_t)blob->bv_ga.ga_len) return FAIL; return OK; } /* * Write "blob" to file "fd". * Return OK or FAIL. */ int write_blob(FILE *fd, blob_T *blob) { if (fwrite(blob->bv_ga.ga_data, 1, blob->bv_ga.ga_len, fd) < (size_t)blob->bv_ga.ga_len) { emsg(_(e_write)); return FAIL; } return OK; } /* * Convert a blob to a readable form: "0z00112233.44556677.8899" */ char_u * blob2string(blob_T *blob, char_u **tofree, char_u *numbuf) { int i; garray_T ga; if (blob == NULL) { *tofree = NULL; return (char_u *)"0z"; } // Store bytes in the growarray. ga_init2(&ga, 1, 4000); ga_concat(&ga, (char_u *)"0z"); for (i = 0; i < blob_len(blob); i++) { if (i > 0 && (i & 3) == 0) ga_concat(&ga, (char_u *)"."); vim_snprintf((char *)numbuf, NUMBUFLEN, "%02X", (int)blob_get(blob, i)); ga_concat(&ga, numbuf); } *tofree = ga.ga_data; return *tofree; } /* * Convert a string variable, in the format of blob2string(), to a blob. * Return NULL when conversion failed. */ blob_T * string2blob(char_u *str) { blob_T *blob = blob_alloc(); char_u *s = str; if (blob == NULL) return NULL; if (s[0] != '0' || (s[1] != 'z' && s[1] != 'Z')) goto failed; s += 2; while (vim_isxdigit(*s)) { if (!vim_isxdigit(s[1])) goto failed; ga_append(&blob->bv_ga, (hex2nr(s[0]) << 4) + hex2nr(s[1])); s += 2; if (*s == '.' && vim_isxdigit(s[1])) ++s; } if (*skipwhite(s) != NUL) goto failed; // text after final digit ++blob->bv_refcount; return blob; failed: blob_free(blob); return NULL; } int blob_slice_or_index( blob_T *blob, int is_range, varnumber_T n1, varnumber_T n2, int exclusive, typval_T *rettv) { long len = blob_len(blob); if (is_range) { // The resulting variable is a sub-blob. If the indexes // are out of range the result is empty. if (n1 < 0) { n1 = len + n1; if (n1 < 0) n1 = 0; } if (n2 < 0) n2 = len + n2; else if (n2 >= len) n2 = len - (exclusive ? 0 : 1); if (exclusive) --n2; if (n1 >= len || n2 < 0 || n1 > n2) { clear_tv(rettv); rettv->v_type = VAR_BLOB; rettv->vval.v_blob = NULL; } else { blob_T *new_blob = blob_alloc(); long i; if (new_blob != NULL) { if (ga_grow(&new_blob->bv_ga, n2 - n1 + 1) == FAIL) { blob_free(new_blob); return FAIL; } new_blob->bv_ga.ga_len = n2 - n1 + 1; for (i = n1; i <= n2; i++) blob_set(new_blob, i - n1, blob_get(blob, i)); clear_tv(rettv); rettv_blob_set(rettv, new_blob); } } } else { // The resulting variable is a byte value. // If the index is too big or negative that is an error. if (n1 < 0) n1 = len + n1; if (n1 < len && n1 >= 0) { int v = blob_get(blob, n1); clear_tv(rettv); rettv->v_type = VAR_NUMBER; rettv->vval.v_number = v; } else { semsg(_(e_blobidx), n1); return FAIL; } } return OK; } /* * Check if "n1"- is a valid index for a blobl with length "bloblen". */ int check_blob_index(long bloblen, varnumber_T n1, int quiet) { if (n1 < 0 || n1 > bloblen) { if (!quiet) semsg(_(e_blobidx), n1); return FAIL; } return OK; } /* * Check if "n1"-"n2" is a valid range for a blob with length "bloblen". */ int check_blob_range(long bloblen, varnumber_T n1, varnumber_T n2, int quiet) { if (n2 < 0 || n2 >= bloblen || n2 < n1) { if (!quiet) semsg(_(e_blobidx), n2); return FAIL; } return OK; } /* * Set bytes "n1" to "n2" (inclusive) in "dest" to the value of "src". * Caller must make sure "src" is a blob. * Returns FAIL if the number of bytes does not match. */ int blob_set_range(blob_T *dest, long n1, long n2, typval_T *src) { int il, ir; if (n2 - n1 + 1 != blob_len(src->vval.v_blob)) { emsg(_("E972: Blob value does not have the right number of bytes")); return FAIL; } ir = 0; for (il = n1; il <= n2; il++) blob_set(dest, il, blob_get(src->vval.v_blob, ir++)); return OK; } /* * "remove({blob})" function */ void blob_remove(typval_T *argvars, typval_T *rettv) { int error = FALSE; long idx = (long)tv_get_number_chk(&argvars[1], &error); long end; if (!error) { blob_T *b = argvars[0].vval.v_blob; int len = blob_len(b); char_u *p; if (idx < 0) // count from the end idx = len + idx; if (idx < 0 || idx >= len) { semsg(_(e_blobidx), idx); return; } if (argvars[2].v_type == VAR_UNKNOWN) { // Remove one item, return its value. p = (char_u *)b->bv_ga.ga_data; rettv->vval.v_number = (varnumber_T) *(p + idx); mch_memmove(p + idx, p + idx + 1, (size_t)len - idx - 1); --b->bv_ga.ga_len; } else { blob_T *blob; // Remove range of items, return blob with values. end = (long)tv_get_number_chk(&argvars[2], &error); if (error) return; if (end < 0) // count from the end end = len + end; if (end >= len || idx > end) { semsg(_(e_blobidx), end); return; } blob = blob_alloc(); if (blob == NULL) return; blob->bv_ga.ga_len = end - idx + 1; if (ga_grow(&blob->bv_ga, end - idx + 1) == FAIL) { vim_free(blob); return; } p = (char_u *)b->bv_ga.ga_data; mch_memmove((char_u *)blob->bv_ga.ga_data, p + idx, (size_t)(end - idx + 1)); ++blob->bv_refcount; rettv->v_type = VAR_BLOB; rettv->vval.v_blob = blob; if (len - end - 1 > 0) mch_memmove(p + idx, p + end + 1, (size_t)(len - end - 1)); b->bv_ga.ga_len -= end - idx + 1; } } } #endif // defined(FEAT_EVAL)