blob: 264962e197d93871aa4c0385a13e05bb31a41f65 [file] [log] [blame]
Bram Moolenaar6e5ea8d2019-01-12 22:47:31 +01001/* 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 *
23blob_alloc(void)
24{
Bram Moolenaarc799fe22019-05-28 23:08:19 +020025 blob_T *blob = ALLOC_CLEAR_ONE(blob_T);
Bram Moolenaar6e5ea8d2019-01-12 22:47:31 +010026
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
37rettv_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
52rettv_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
Bram Moolenaardd29ea12019-01-23 21:56:21 +010060 int
Bram Moolenaar8a7d6542020-01-26 15:56:19 +010061blob_copy(blob_T *from, typval_T *to)
Bram Moolenaardd29ea12019-01-23 21:56:21 +010062{
63 int ret = OK;
64
65 to->v_type = VAR_BLOB;
Bram Moolenaarb7b9efb2019-07-12 20:17:03 +020066 to->v_lock = 0;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +010067 if (from == NULL)
Bram Moolenaardd29ea12019-01-23 21:56:21 +010068 to->vval.v_blob = NULL;
69 else if (rettv_blob_alloc(to) == FAIL)
70 ret = FAIL;
71 else
72 {
Bram Moolenaar8a7d6542020-01-26 15:56:19 +010073 int len = from->bv_ga.ga_len;
Bram Moolenaardd29ea12019-01-23 21:56:21 +010074
75 if (len > 0)
Bram Moolenaara5be9b62019-01-24 12:31:44 +010076 {
Bram Moolenaardd29ea12019-01-23 21:56:21 +010077 to->vval.v_blob->bv_ga.ga_data =
Bram Moolenaar8a7d6542020-01-26 15:56:19 +010078 vim_memsave(from->bv_ga.ga_data, len);
Bram Moolenaara5be9b62019-01-24 12:31:44 +010079 if (to->vval.v_blob->bv_ga.ga_data == NULL)
80 len = 0;
81 }
Bram Moolenaardd29ea12019-01-23 21:56:21 +010082 to->vval.v_blob->bv_ga.ga_len = len;
Bram Moolenaar66fa5fd2020-10-19 20:21:03 +020083 to->vval.v_blob->bv_ga.ga_maxlen = len;
Bram Moolenaardd29ea12019-01-23 21:56:21 +010084 }
85 return ret;
86}
87
Bram Moolenaar6e5ea8d2019-01-12 22:47:31 +010088 void
89blob_free(blob_T *b)
90{
91 ga_clear(&b->bv_ga);
92 vim_free(b);
93}
94
95/*
96 * Unreference a blob: decrement the reference count and free it when it
97 * becomes zero.
98 */
99 void
100blob_unref(blob_T *b)
101{
102 if (b != NULL && --b->bv_refcount <= 0)
103 blob_free(b);
104}
105
106/*
107 * Get the length of data.
108 */
109 long
110blob_len(blob_T *b)
111{
112 if (b == NULL)
113 return 0L;
114 return b->bv_ga.ga_len;
115}
116
117/*
118 * Get byte "idx" in blob "b".
119 * Caller must check that "idx" is valid.
120 */
Bram Moolenaara5be9b62019-01-24 12:31:44 +0100121 int
Bram Moolenaar6e5ea8d2019-01-12 22:47:31 +0100122blob_get(blob_T *b, int idx)
123{
124 return ((char_u*)b->bv_ga.ga_data)[idx];
125}
126
127/*
128 * Store one byte "c" in blob "b" at "idx".
129 * Caller must make sure that "idx" is valid.
130 */
131 void
132blob_set(blob_T *b, int idx, char_u c)
133{
134 ((char_u*)b->bv_ga.ga_data)[idx] = c;
135}
136
137/*
138 * Return TRUE when two blobs have exactly the same values.
139 */
140 int
141blob_equal(
142 blob_T *b1,
143 blob_T *b2)
144{
Bram Moolenaarc0f5a782019-01-13 15:16:13 +0100145 int i;
146 int len1 = blob_len(b1);
147 int len2 = blob_len(b2);
Bram Moolenaar6e5ea8d2019-01-12 22:47:31 +0100148
Bram Moolenaarc0f5a782019-01-13 15:16:13 +0100149 // empty and NULL are considered the same
150 if (len1 == 0 && len2 == 0)
151 return TRUE;
Bram Moolenaar6e5ea8d2019-01-12 22:47:31 +0100152 if (b1 == b2)
153 return TRUE;
Bram Moolenaarc0f5a782019-01-13 15:16:13 +0100154 if (len1 != len2)
Bram Moolenaar6e5ea8d2019-01-12 22:47:31 +0100155 return FALSE;
156
157 for (i = 0; i < b1->bv_ga.ga_len; i++)
158 if (blob_get(b1, i) != blob_get(b2, i)) return FALSE;
159 return TRUE;
160}
161
162/*
163 * Read "blob" from file "fd".
164 * Return OK or FAIL.
165 */
166 int
167read_blob(FILE *fd, blob_T *blob)
168{
169 struct stat st;
170
171 if (fstat(fileno(fd), &st) < 0)
172 return FAIL;
173 if (ga_grow(&blob->bv_ga, st.st_size) == FAIL)
174 return FAIL;
175 blob->bv_ga.ga_len = st.st_size;
176 if (fread(blob->bv_ga.ga_data, 1, blob->bv_ga.ga_len, fd)
177 < (size_t)blob->bv_ga.ga_len)
178 return FAIL;
179 return OK;
180}
181
182/*
183 * Write "blob" to file "fd".
184 * Return OK or FAIL.
185 */
186 int
187write_blob(FILE *fd, blob_T *blob)
188{
189 if (fwrite(blob->bv_ga.ga_data, 1, blob->bv_ga.ga_len, fd)
190 < (size_t)blob->bv_ga.ga_len)
191 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100192 emsg(_(e_write));
Bram Moolenaar6e5ea8d2019-01-12 22:47:31 +0100193 return FAIL;
194 }
195 return OK;
196}
197
Bram Moolenaar8c8b8bb2019-01-13 17:48:04 +0100198/*
Bram Moolenaar4131fd52019-01-17 16:32:53 +0100199 * Convert a blob to a readable form: "0z00112233.44556677.8899"
Bram Moolenaar8c8b8bb2019-01-13 17:48:04 +0100200 */
201 char_u *
202blob2string(blob_T *blob, char_u **tofree, char_u *numbuf)
203{
204 int i;
205 garray_T ga;
206
207 if (blob == NULL)
208 {
209 *tofree = NULL;
Bram Moolenaar4131fd52019-01-17 16:32:53 +0100210 return (char_u *)"0z";
Bram Moolenaar8c8b8bb2019-01-13 17:48:04 +0100211 }
212
213 // Store bytes in the growarray.
214 ga_init2(&ga, 1, 4000);
Bram Moolenaar4131fd52019-01-17 16:32:53 +0100215 ga_concat(&ga, (char_u *)"0z");
Bram Moolenaar8c8b8bb2019-01-13 17:48:04 +0100216 for (i = 0; i < blob_len(blob); i++)
217 {
Bram Moolenaar4131fd52019-01-17 16:32:53 +0100218 if (i > 0 && (i & 3) == 0)
219 ga_concat(&ga, (char_u *)".");
220 vim_snprintf((char *)numbuf, NUMBUFLEN, "%02X", (int)blob_get(blob, i));
Bram Moolenaar8c8b8bb2019-01-13 17:48:04 +0100221 ga_concat(&ga, numbuf);
222 }
Bram Moolenaar8c8b8bb2019-01-13 17:48:04 +0100223 *tofree = ga.ga_data;
224 return *tofree;
225}
226
227/*
228 * Convert a string variable, in the format of blob2string(), to a blob.
229 * Return NULL when conversion failed.
230 */
231 blob_T *
232string2blob(char_u *str)
233{
234 blob_T *blob = blob_alloc();
235 char_u *s = str;
236
Bram Moolenaare142a942019-03-19 23:03:27 +0100237 if (blob == NULL)
238 return NULL;
Bram Moolenaar4131fd52019-01-17 16:32:53 +0100239 if (s[0] != '0' || (s[1] != 'z' && s[1] != 'Z'))
Bram Moolenaar8c8b8bb2019-01-13 17:48:04 +0100240 goto failed;
Bram Moolenaar4131fd52019-01-17 16:32:53 +0100241 s += 2;
242 while (vim_isxdigit(*s))
Bram Moolenaar8c8b8bb2019-01-13 17:48:04 +0100243 {
Bram Moolenaar4131fd52019-01-17 16:32:53 +0100244 if (!vim_isxdigit(s[1]))
Bram Moolenaar8c8b8bb2019-01-13 17:48:04 +0100245 goto failed;
Bram Moolenaar4131fd52019-01-17 16:32:53 +0100246 ga_append(&blob->bv_ga, (hex2nr(s[0]) << 4) + hex2nr(s[1]));
247 s += 2;
248 if (*s == '.' && vim_isxdigit(s[1]))
249 ++s;
Bram Moolenaar8c8b8bb2019-01-13 17:48:04 +0100250 }
Bram Moolenaar4131fd52019-01-17 16:32:53 +0100251 if (*skipwhite(s) != NUL)
252 goto failed; // text after final digit
Bram Moolenaar8c8b8bb2019-01-13 17:48:04 +0100253
254 ++blob->bv_refcount;
255 return blob;
256
257failed:
258 blob_free(blob);
259 return NULL;
260}
261
Bram Moolenaar9f9fe372019-07-27 23:12:12 +0200262/*
263 * "remove({blob})" function
264 */
265 void
266blob_remove(typval_T *argvars, typval_T *rettv)
267{
268 int error = FALSE;
269 long idx = (long)tv_get_number_chk(&argvars[1], &error);
270 long end;
271
272 if (!error)
273 {
274 blob_T *b = argvars[0].vval.v_blob;
275 int len = blob_len(b);
276 char_u *p;
277
278 if (idx < 0)
279 // count from the end
280 idx = len + idx;
281 if (idx < 0 || idx >= len)
282 {
283 semsg(_(e_blobidx), idx);
284 return;
285 }
286 if (argvars[2].v_type == VAR_UNKNOWN)
287 {
288 // Remove one item, return its value.
289 p = (char_u *)b->bv_ga.ga_data;
290 rettv->vval.v_number = (varnumber_T) *(p + idx);
291 mch_memmove(p + idx, p + idx + 1, (size_t)len - idx - 1);
292 --b->bv_ga.ga_len;
293 }
294 else
295 {
296 blob_T *blob;
297
298 // Remove range of items, return list with values.
299 end = (long)tv_get_number_chk(&argvars[2], &error);
300 if (error)
301 return;
302 if (end < 0)
303 // count from the end
304 end = len + end;
305 if (end >= len || idx > end)
306 {
307 semsg(_(e_blobidx), end);
308 return;
309 }
310 blob = blob_alloc();
311 if (blob == NULL)
312 return;
313 blob->bv_ga.ga_len = end - idx + 1;
314 if (ga_grow(&blob->bv_ga, end - idx + 1) == FAIL)
315 {
316 vim_free(blob);
317 return;
318 }
319 p = (char_u *)b->bv_ga.ga_data;
320 mch_memmove((char_u *)blob->bv_ga.ga_data, p + idx,
321 (size_t)(end - idx + 1));
322 ++blob->bv_refcount;
323 rettv->v_type = VAR_BLOB;
324 rettv->vval.v_blob = blob;
325
326 mch_memmove(p + idx, p + end + 1, (size_t)(len - end));
327 b->bv_ga.ga_len -= end - idx + 1;
328 }
329 }
330}
331
Bram Moolenaarc667da52019-11-30 20:52:27 +0100332#endif // defined(FEAT_EVAL)