blob: 2a7ec3a73ab64a23919a4649ab3d916ec7cdff72 [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
61blob_copy(typval_T *from, typval_T *to)
62{
63 int ret = OK;
64
65 to->v_type = VAR_BLOB;
Bram Moolenaarb7b9efb2019-07-12 20:17:03 +020066 to->v_lock = 0;
Bram Moolenaardd29ea12019-01-23 21:56:21 +010067 if (from->vval.v_blob == 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->vval.v_blob->bv_ga.ga_len;
74
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 =
78 vim_memsave(from->vval.v_blob->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;
83 }
84 return ret;
85}
86
Bram Moolenaar6e5ea8d2019-01-12 22:47:31 +010087 void
88blob_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
99blob_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
109blob_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 */
Bram Moolenaara5be9b62019-01-24 12:31:44 +0100120 int
Bram Moolenaar6e5ea8d2019-01-12 22:47:31 +0100121blob_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
131blob_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
140blob_equal(
141 blob_T *b1,
142 blob_T *b2)
143{
Bram Moolenaarc0f5a782019-01-13 15:16:13 +0100144 int i;
145 int len1 = blob_len(b1);
146 int len2 = blob_len(b2);
Bram Moolenaar6e5ea8d2019-01-12 22:47:31 +0100147
Bram Moolenaarc0f5a782019-01-13 15:16:13 +0100148 // empty and NULL are considered the same
149 if (len1 == 0 && len2 == 0)
150 return TRUE;
Bram Moolenaar6e5ea8d2019-01-12 22:47:31 +0100151 if (b1 == b2)
152 return TRUE;
Bram Moolenaarc0f5a782019-01-13 15:16:13 +0100153 if (len1 != len2)
Bram Moolenaar6e5ea8d2019-01-12 22:47:31 +0100154 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
166read_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
186write_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 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100191 emsg(_(e_write));
Bram Moolenaar6e5ea8d2019-01-12 22:47:31 +0100192 return FAIL;
193 }
194 return OK;
195}
196
Bram Moolenaar8c8b8bb2019-01-13 17:48:04 +0100197/*
Bram Moolenaar4131fd52019-01-17 16:32:53 +0100198 * Convert a blob to a readable form: "0z00112233.44556677.8899"
Bram Moolenaar8c8b8bb2019-01-13 17:48:04 +0100199 */
200 char_u *
201blob2string(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;
Bram Moolenaar4131fd52019-01-17 16:32:53 +0100209 return (char_u *)"0z";
Bram Moolenaar8c8b8bb2019-01-13 17:48:04 +0100210 }
211
212 // Store bytes in the growarray.
213 ga_init2(&ga, 1, 4000);
Bram Moolenaar4131fd52019-01-17 16:32:53 +0100214 ga_concat(&ga, (char_u *)"0z");
Bram Moolenaar8c8b8bb2019-01-13 17:48:04 +0100215 for (i = 0; i < blob_len(blob); i++)
216 {
Bram Moolenaar4131fd52019-01-17 16:32:53 +0100217 if (i > 0 && (i & 3) == 0)
218 ga_concat(&ga, (char_u *)".");
219 vim_snprintf((char *)numbuf, NUMBUFLEN, "%02X", (int)blob_get(blob, i));
Bram Moolenaar8c8b8bb2019-01-13 17:48:04 +0100220 ga_concat(&ga, numbuf);
221 }
Bram Moolenaar8c8b8bb2019-01-13 17:48:04 +0100222 *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 *
231string2blob(char_u *str)
232{
233 blob_T *blob = blob_alloc();
234 char_u *s = str;
235
Bram Moolenaare142a942019-03-19 23:03:27 +0100236 if (blob == NULL)
237 return NULL;
Bram Moolenaar4131fd52019-01-17 16:32:53 +0100238 if (s[0] != '0' || (s[1] != 'z' && s[1] != 'Z'))
Bram Moolenaar8c8b8bb2019-01-13 17:48:04 +0100239 goto failed;
Bram Moolenaar4131fd52019-01-17 16:32:53 +0100240 s += 2;
241 while (vim_isxdigit(*s))
Bram Moolenaar8c8b8bb2019-01-13 17:48:04 +0100242 {
Bram Moolenaar4131fd52019-01-17 16:32:53 +0100243 if (!vim_isxdigit(s[1]))
Bram Moolenaar8c8b8bb2019-01-13 17:48:04 +0100244 goto failed;
Bram Moolenaar4131fd52019-01-17 16:32:53 +0100245 ga_append(&blob->bv_ga, (hex2nr(s[0]) << 4) + hex2nr(s[1]));
246 s += 2;
247 if (*s == '.' && vim_isxdigit(s[1]))
248 ++s;
Bram Moolenaar8c8b8bb2019-01-13 17:48:04 +0100249 }
Bram Moolenaar4131fd52019-01-17 16:32:53 +0100250 if (*skipwhite(s) != NUL)
251 goto failed; // text after final digit
Bram Moolenaar8c8b8bb2019-01-13 17:48:04 +0100252
253 ++blob->bv_refcount;
254 return blob;
255
256failed:
257 blob_free(blob);
258 return NULL;
259}
260
Bram Moolenaar9f9fe372019-07-27 23:12:12 +0200261/*
262 * "remove({blob})" function
263 */
264 void
265blob_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
Bram Moolenaarc667da52019-11-30 20:52:27 +0100331#endif // defined(FEAT_EVAL)