Vim/src/blob.c

332 lines
6.2 KiB
C

/* 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;
}
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 "c" in blob "b" at "idx".
* Caller must make sure that "idx" is valid.
*/
void
blob_set(blob_T *b, int idx, char_u c)
{
((char_u*)b->bv_ga.ga_data)[idx] = c;
}
/*
* 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;
}
/*
* "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 list 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;
mch_memmove(p + idx, p + end + 1, (size_t)(len - end));
b->bv_ga.ga_len -= end - idx + 1;
}
}
}
#endif // defined(FEAT_EVAL)