xref: /vim-8.2.3635/src/blob.c (revision ed37d9b3)
1 /* vi:set ts=8 sts=4 sw=4 noet:
2  *
3  * VIM - Vi IMproved	by Bram Moolenaar
4  *
5  * Do ":help uganda"  in Vim to read copying and usage conditions.
6  * Do ":help credits" in Vim to see a list of people who contributed.
7  * See README.txt for an overview of the Vim source code.
8  */
9 
10 /*
11  * blob.c: Blob support by Yasuhiro Matsumoto
12  */
13 
14 #include "vim.h"
15 
16 #if defined(FEAT_EVAL) || defined(PROTO)
17 
18 /*
19  * Allocate an empty blob.
20  * Caller should take care of the reference count.
21  */
22     blob_T *
23 blob_alloc(void)
24 {
25     blob_T *blob = ALLOC_CLEAR_ONE(blob_T);
26 
27     if (blob != NULL)
28 	ga_init2(&blob->bv_ga, 1, 100);
29     return blob;
30 }
31 
32 /*
33  * Allocate an empty blob for a return value, with reference count set.
34  * Returns OK or FAIL.
35  */
36     int
37 rettv_blob_alloc(typval_T *rettv)
38 {
39     blob_T	*b = blob_alloc();
40 
41     if (b == NULL)
42 	return FAIL;
43 
44     rettv_blob_set(rettv, b);
45     return OK;
46 }
47 
48 /*
49  * Set a blob as the return value.
50  */
51     void
52 rettv_blob_set(typval_T *rettv, blob_T *b)
53 {
54     rettv->v_type = VAR_BLOB;
55     rettv->vval.v_blob = b;
56     if (b != NULL)
57 	++b->bv_refcount;
58 }
59 
60     int
61 blob_copy(blob_T *from, typval_T *to)
62 {
63     int	    ret = OK;
64 
65     to->v_type = VAR_BLOB;
66     to->v_lock = 0;
67     if (from == NULL)
68 	to->vval.v_blob = NULL;
69     else if (rettv_blob_alloc(to) == FAIL)
70 	ret = FAIL;
71     else
72     {
73 	int  len = from->bv_ga.ga_len;
74 
75 	if (len > 0)
76 	{
77 	    to->vval.v_blob->bv_ga.ga_data =
78 					 vim_memsave(from->bv_ga.ga_data, len);
79 	    if (to->vval.v_blob->bv_ga.ga_data == NULL)
80 		len = 0;
81 	}
82 	to->vval.v_blob->bv_ga.ga_len = len;
83     }
84     return ret;
85 }
86 
87     void
88 blob_free(blob_T *b)
89 {
90     ga_clear(&b->bv_ga);
91     vim_free(b);
92 }
93 
94 /*
95  * Unreference a blob: decrement the reference count and free it when it
96  * becomes zero.
97  */
98     void
99 blob_unref(blob_T *b)
100 {
101     if (b != NULL && --b->bv_refcount <= 0)
102 	blob_free(b);
103 }
104 
105 /*
106  * Get the length of data.
107  */
108     long
109 blob_len(blob_T *b)
110 {
111     if (b == NULL)
112 	return 0L;
113     return b->bv_ga.ga_len;
114 }
115 
116 /*
117  * Get byte "idx" in blob "b".
118  * Caller must check that "idx" is valid.
119  */
120     int
121 blob_get(blob_T *b, int idx)
122 {
123     return ((char_u*)b->bv_ga.ga_data)[idx];
124 }
125 
126 /*
127  * Store one byte "c" in blob "b" at "idx".
128  * Caller must make sure that "idx" is valid.
129  */
130     void
131 blob_set(blob_T *b, int idx, char_u c)
132 {
133     ((char_u*)b->bv_ga.ga_data)[idx] = c;
134 }
135 
136 /*
137  * Return TRUE when two blobs have exactly the same values.
138  */
139     int
140 blob_equal(
141     blob_T	*b1,
142     blob_T	*b2)
143 {
144     int	    i;
145     int	    len1 = blob_len(b1);
146     int	    len2 = blob_len(b2);
147 
148     // empty and NULL are considered the same
149     if (len1 == 0 && len2 == 0)
150 	return TRUE;
151     if (b1 == b2)
152 	return TRUE;
153     if (len1 != len2)
154 	return FALSE;
155 
156     for (i = 0; i < b1->bv_ga.ga_len; i++)
157 	if (blob_get(b1, i) != blob_get(b2, i)) return FALSE;
158     return TRUE;
159 }
160 
161 /*
162  * Read "blob" from file "fd".
163  * Return OK or FAIL.
164  */
165     int
166 read_blob(FILE *fd, blob_T *blob)
167 {
168     struct stat	st;
169 
170     if (fstat(fileno(fd), &st) < 0)
171 	return FAIL;
172     if (ga_grow(&blob->bv_ga, st.st_size) == FAIL)
173 	return FAIL;
174     blob->bv_ga.ga_len = st.st_size;
175     if (fread(blob->bv_ga.ga_data, 1, blob->bv_ga.ga_len, fd)
176 						  < (size_t)blob->bv_ga.ga_len)
177 	return FAIL;
178     return OK;
179 }
180 
181 /*
182  * Write "blob" to file "fd".
183  * Return OK or FAIL.
184  */
185     int
186 write_blob(FILE *fd, blob_T *blob)
187 {
188     if (fwrite(blob->bv_ga.ga_data, 1, blob->bv_ga.ga_len, fd)
189 						  < (size_t)blob->bv_ga.ga_len)
190     {
191 	emsg(_(e_write));
192 	return FAIL;
193     }
194     return OK;
195 }
196 
197 /*
198  * Convert a blob to a readable form: "0z00112233.44556677.8899"
199  */
200     char_u *
201 blob2string(blob_T *blob, char_u **tofree, char_u *numbuf)
202 {
203     int		i;
204     garray_T    ga;
205 
206     if (blob == NULL)
207     {
208 	*tofree = NULL;
209 	return (char_u *)"0z";
210     }
211 
212     // Store bytes in the growarray.
213     ga_init2(&ga, 1, 4000);
214     ga_concat(&ga, (char_u *)"0z");
215     for (i = 0; i < blob_len(blob); i++)
216     {
217 	if (i > 0 && (i & 3) == 0)
218 	    ga_concat(&ga, (char_u *)".");
219 	vim_snprintf((char *)numbuf, NUMBUFLEN, "%02X", (int)blob_get(blob, i));
220 	ga_concat(&ga, numbuf);
221     }
222     *tofree = ga.ga_data;
223     return *tofree;
224 }
225 
226 /*
227  * Convert a string variable, in the format of blob2string(), to a blob.
228  * Return NULL when conversion failed.
229  */
230     blob_T *
231 string2blob(char_u *str)
232 {
233     blob_T  *blob = blob_alloc();
234     char_u  *s = str;
235 
236     if (blob == NULL)
237 	return NULL;
238     if (s[0] != '0' || (s[1] != 'z' && s[1] != 'Z'))
239 	goto failed;
240     s += 2;
241     while (vim_isxdigit(*s))
242     {
243 	if (!vim_isxdigit(s[1]))
244 	    goto failed;
245 	ga_append(&blob->bv_ga, (hex2nr(s[0]) << 4) + hex2nr(s[1]));
246 	s += 2;
247 	if (*s == '.' && vim_isxdigit(s[1]))
248 	    ++s;
249     }
250     if (*skipwhite(s) != NUL)
251 	goto failed;  // text after final digit
252 
253     ++blob->bv_refcount;
254     return blob;
255 
256 failed:
257     blob_free(blob);
258     return NULL;
259 }
260 
261 /*
262  * "remove({blob})" function
263  */
264     void
265 blob_remove(typval_T *argvars, typval_T *rettv)
266 {
267     int		error = FALSE;
268     long	idx = (long)tv_get_number_chk(&argvars[1], &error);
269     long	end;
270 
271     if (!error)
272     {
273 	blob_T  *b = argvars[0].vval.v_blob;
274 	int	len = blob_len(b);
275 	char_u  *p;
276 
277 	if (idx < 0)
278 	    // count from the end
279 	    idx = len + idx;
280 	if (idx < 0 || idx >= len)
281 	{
282 	    semsg(_(e_blobidx), idx);
283 	    return;
284 	}
285 	if (argvars[2].v_type == VAR_UNKNOWN)
286 	{
287 	    // Remove one item, return its value.
288 	    p = (char_u *)b->bv_ga.ga_data;
289 	    rettv->vval.v_number = (varnumber_T) *(p + idx);
290 	    mch_memmove(p + idx, p + idx + 1, (size_t)len - idx - 1);
291 	    --b->bv_ga.ga_len;
292 	}
293 	else
294 	{
295 	    blob_T  *blob;
296 
297 	    // Remove range of items, return list with values.
298 	    end = (long)tv_get_number_chk(&argvars[2], &error);
299 	    if (error)
300 		return;
301 	    if (end < 0)
302 		// count from the end
303 		end = len + end;
304 	    if (end >= len || idx > end)
305 	    {
306 		semsg(_(e_blobidx), end);
307 		return;
308 	    }
309 	    blob = blob_alloc();
310 	    if (blob == NULL)
311 		return;
312 	    blob->bv_ga.ga_len = end - idx + 1;
313 	    if (ga_grow(&blob->bv_ga, end - idx + 1) == FAIL)
314 	    {
315 		vim_free(blob);
316 		return;
317 	    }
318 	    p = (char_u *)b->bv_ga.ga_data;
319 	    mch_memmove((char_u *)blob->bv_ga.ga_data, p + idx,
320 						  (size_t)(end - idx + 1));
321 	    ++blob->bv_refcount;
322 	    rettv->v_type = VAR_BLOB;
323 	    rettv->vval.v_blob = blob;
324 
325 	    mch_memmove(p + idx, p + end + 1, (size_t)(len - end));
326 	    b->bv_ga.ga_len -= end - idx + 1;
327 	}
328     }
329 }
330 
331 #endif // defined(FEAT_EVAL)
332