blob: 9dc7926f4426cac7861373cb4796cc7200975792 [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{
25 blob_T *blob = (blob_T *)alloc_clear(sizeof(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
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;
66 if (from->vval.v_blob == NULL)
67 to->vval.v_blob = NULL;
68 else if (rettv_blob_alloc(to) == FAIL)
69 ret = FAIL;
70 else
71 {
72 int len = from->vval.v_blob->bv_ga.ga_len;
73
74 if (len > 0)
Bram Moolenaara5be9b62019-01-24 12:31:44 +010075 {
Bram Moolenaardd29ea12019-01-23 21:56:21 +010076 to->vval.v_blob->bv_ga.ga_data =
77 vim_memsave(from->vval.v_blob->bv_ga.ga_data, len);
Bram Moolenaara5be9b62019-01-24 12:31:44 +010078 if (to->vval.v_blob->bv_ga.ga_data == NULL)
79 len = 0;
80 }
Bram Moolenaardd29ea12019-01-23 21:56:21 +010081 to->vval.v_blob->bv_ga.ga_len = len;
82 }
83 return ret;
84}
85
Bram Moolenaar6e5ea8d2019-01-12 22:47:31 +010086 void
87blob_free(blob_T *b)
88{
89 ga_clear(&b->bv_ga);
90 vim_free(b);
91}
92
93/*
94 * Unreference a blob: decrement the reference count and free it when it
95 * becomes zero.
96 */
97 void
98blob_unref(blob_T *b)
99{
100 if (b != NULL && --b->bv_refcount <= 0)
101 blob_free(b);
102}
103
104/*
105 * Get the length of data.
106 */
107 long
108blob_len(blob_T *b)
109{
110 if (b == NULL)
111 return 0L;
112 return b->bv_ga.ga_len;
113}
114
115/*
116 * Get byte "idx" in blob "b".
117 * Caller must check that "idx" is valid.
118 */
Bram Moolenaara5be9b62019-01-24 12:31:44 +0100119 int
Bram Moolenaar6e5ea8d2019-01-12 22:47:31 +0100120blob_get(blob_T *b, int idx)
121{
122 return ((char_u*)b->bv_ga.ga_data)[idx];
123}
124
125/*
126 * Store one byte "c" in blob "b" at "idx".
127 * Caller must make sure that "idx" is valid.
128 */
129 void
130blob_set(blob_T *b, int idx, char_u c)
131{
132 ((char_u*)b->bv_ga.ga_data)[idx] = c;
133}
134
135/*
136 * Return TRUE when two blobs have exactly the same values.
137 */
138 int
139blob_equal(
140 blob_T *b1,
141 blob_T *b2)
142{
Bram Moolenaarc0f5a782019-01-13 15:16:13 +0100143 int i;
144 int len1 = blob_len(b1);
145 int len2 = blob_len(b2);
Bram Moolenaar6e5ea8d2019-01-12 22:47:31 +0100146
Bram Moolenaarc0f5a782019-01-13 15:16:13 +0100147 // empty and NULL are considered the same
148 if (len1 == 0 && len2 == 0)
149 return TRUE;
Bram Moolenaar6e5ea8d2019-01-12 22:47:31 +0100150 if (b1 == b2)
151 return TRUE;
Bram Moolenaarc0f5a782019-01-13 15:16:13 +0100152 if (len1 != len2)
Bram Moolenaar6e5ea8d2019-01-12 22:47:31 +0100153 return FALSE;
154
155 for (i = 0; i < b1->bv_ga.ga_len; i++)
156 if (blob_get(b1, i) != blob_get(b2, i)) return FALSE;
157 return TRUE;
158}
159
160/*
161 * Read "blob" from file "fd".
162 * Return OK or FAIL.
163 */
164 int
165read_blob(FILE *fd, blob_T *blob)
166{
167 struct stat st;
168
169 if (fstat(fileno(fd), &st) < 0)
170 return FAIL;
171 if (ga_grow(&blob->bv_ga, st.st_size) == FAIL)
172 return FAIL;
173 blob->bv_ga.ga_len = st.st_size;
174 if (fread(blob->bv_ga.ga_data, 1, blob->bv_ga.ga_len, fd)
175 < (size_t)blob->bv_ga.ga_len)
176 return FAIL;
177 return OK;
178}
179
180/*
181 * Write "blob" to file "fd".
182 * Return OK or FAIL.
183 */
184 int
185write_blob(FILE *fd, blob_T *blob)
186{
187 if (fwrite(blob->bv_ga.ga_data, 1, blob->bv_ga.ga_len, fd)
188 < (size_t)blob->bv_ga.ga_len)
189 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100190 emsg(_(e_write));
Bram Moolenaar6e5ea8d2019-01-12 22:47:31 +0100191 return FAIL;
192 }
193 return OK;
194}
195
Bram Moolenaar8c8b8bb2019-01-13 17:48:04 +0100196/*
Bram Moolenaar4131fd52019-01-17 16:32:53 +0100197 * Convert a blob to a readable form: "0z00112233.44556677.8899"
Bram Moolenaar8c8b8bb2019-01-13 17:48:04 +0100198 */
199 char_u *
200blob2string(blob_T *blob, char_u **tofree, char_u *numbuf)
201{
202 int i;
203 garray_T ga;
204
205 if (blob == NULL)
206 {
207 *tofree = NULL;
Bram Moolenaar4131fd52019-01-17 16:32:53 +0100208 return (char_u *)"0z";
Bram Moolenaar8c8b8bb2019-01-13 17:48:04 +0100209 }
210
211 // Store bytes in the growarray.
212 ga_init2(&ga, 1, 4000);
Bram Moolenaar4131fd52019-01-17 16:32:53 +0100213 ga_concat(&ga, (char_u *)"0z");
Bram Moolenaar8c8b8bb2019-01-13 17:48:04 +0100214 for (i = 0; i < blob_len(blob); i++)
215 {
Bram Moolenaar4131fd52019-01-17 16:32:53 +0100216 if (i > 0 && (i & 3) == 0)
217 ga_concat(&ga, (char_u *)".");
218 vim_snprintf((char *)numbuf, NUMBUFLEN, "%02X", (int)blob_get(blob, i));
Bram Moolenaar8c8b8bb2019-01-13 17:48:04 +0100219 ga_concat(&ga, numbuf);
220 }
Bram Moolenaar8c8b8bb2019-01-13 17:48:04 +0100221 *tofree = ga.ga_data;
222 return *tofree;
223}
224
225/*
226 * Convert a string variable, in the format of blob2string(), to a blob.
227 * Return NULL when conversion failed.
228 */
229 blob_T *
230string2blob(char_u *str)
231{
232 blob_T *blob = blob_alloc();
233 char_u *s = str;
234
Bram Moolenaar4131fd52019-01-17 16:32:53 +0100235 if (s[0] != '0' || (s[1] != 'z' && s[1] != 'Z'))
Bram Moolenaar8c8b8bb2019-01-13 17:48:04 +0100236 goto failed;
Bram Moolenaar4131fd52019-01-17 16:32:53 +0100237 s += 2;
238 while (vim_isxdigit(*s))
Bram Moolenaar8c8b8bb2019-01-13 17:48:04 +0100239 {
Bram Moolenaar4131fd52019-01-17 16:32:53 +0100240 if (!vim_isxdigit(s[1]))
Bram Moolenaar8c8b8bb2019-01-13 17:48:04 +0100241 goto failed;
Bram Moolenaar4131fd52019-01-17 16:32:53 +0100242 ga_append(&blob->bv_ga, (hex2nr(s[0]) << 4) + hex2nr(s[1]));
243 s += 2;
244 if (*s == '.' && vim_isxdigit(s[1]))
245 ++s;
Bram Moolenaar8c8b8bb2019-01-13 17:48:04 +0100246 }
Bram Moolenaar4131fd52019-01-17 16:32:53 +0100247 if (*skipwhite(s) != NUL)
248 goto failed; // text after final digit
Bram Moolenaar8c8b8bb2019-01-13 17:48:04 +0100249
250 ++blob->bv_refcount;
251 return blob;
252
253failed:
254 blob_free(blob);
255 return NULL;
256}
257
Bram Moolenaar6e5ea8d2019-01-12 22:47:31 +0100258#endif /* defined(FEAT_EVAL) */