/* 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_ID(blob_T, aid_blob_alloc);
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 len;
to->v_type = VAR_BLOB;
to->v_lock = 0;
if (from == NULL)
{
to->vval.v_blob = NULL;
return OK;
}
if (rettv_blob_alloc(to) == FAIL)
return FAIL;
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 OK;
}
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".
* Caller has allocated a blob in "rettv".
* Return OK or FAIL.
*/
int
read_blob(FILE *fd, typval_T *rettv, off_T offset, off_T size_arg)
{
blob_T *blob = rettv->vval.v_blob;
struct stat st;
int whence;
off_T size = size_arg;
if (fstat(fileno(fd), &st) < 0)
return FAIL; // can't read the file, error
if (offset >= 0)
{
// The size defaults to the whole file. If a size is given it is
// limited to not go past the end of the file.
if (size == -1 || (size > st.st_size - offset
#ifdef S_ISCHR
&& !S_ISCHR(st.st_mode)
#endif
))
// size may become negative, checked below
size = st.st_size - offset;
whence = SEEK_SET;
}
else
{
// limit the offset to not go before the start of the file
if (-offset > st.st_size
#ifdef S_ISCHR
&& !S_ISCHR(st.st_mode)
#endif
)
offset =