blob: 6b771fed1f4eba2eed29b0602ffef2ce5777304f [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
60 void
61blob_free(blob_T *b)
62{
63 ga_clear(&b->bv_ga);
64 vim_free(b);
65}
66
67/*
68 * Unreference a blob: decrement the reference count and free it when it
69 * becomes zero.
70 */
71 void
72blob_unref(blob_T *b)
73{
74 if (b != NULL && --b->bv_refcount <= 0)
75 blob_free(b);
76}
77
78/*
79 * Get the length of data.
80 */
81 long
82blob_len(blob_T *b)
83{
84 if (b == NULL)
85 return 0L;
86 return b->bv_ga.ga_len;
87}
88
89/*
90 * Get byte "idx" in blob "b".
91 * Caller must check that "idx" is valid.
92 */
93 char_u
94blob_get(blob_T *b, int idx)
95{
96 return ((char_u*)b->bv_ga.ga_data)[idx];
97}
98
99/*
100 * Store one byte "c" in blob "b" at "idx".
101 * Caller must make sure that "idx" is valid.
102 */
103 void
104blob_set(blob_T *b, int idx, char_u c)
105{
106 ((char_u*)b->bv_ga.ga_data)[idx] = c;
107}
108
109/*
110 * Return TRUE when two blobs have exactly the same values.
111 */
112 int
113blob_equal(
114 blob_T *b1,
115 blob_T *b2)
116{
Bram Moolenaarc0f5a782019-01-13 15:16:13 +0100117 int i;
118 int len1 = blob_len(b1);
119 int len2 = blob_len(b2);
Bram Moolenaar6e5ea8d2019-01-12 22:47:31 +0100120
Bram Moolenaarc0f5a782019-01-13 15:16:13 +0100121 // empty and NULL are considered the same
122 if (len1 == 0 && len2 == 0)
123 return TRUE;
Bram Moolenaar6e5ea8d2019-01-12 22:47:31 +0100124 if (b1 == b2)
125 return TRUE;
Bram Moolenaarc0f5a782019-01-13 15:16:13 +0100126 if (len1 != len2)
Bram Moolenaar6e5ea8d2019-01-12 22:47:31 +0100127 return FALSE;
128
129 for (i = 0; i < b1->bv_ga.ga_len; i++)
130 if (blob_get(b1, i) != blob_get(b2, i)) return FALSE;
131 return TRUE;
132}
133
134/*
135 * Read "blob" from file "fd".
136 * Return OK or FAIL.
137 */
138 int
139read_blob(FILE *fd, blob_T *blob)
140{
141 struct stat st;
142
143 if (fstat(fileno(fd), &st) < 0)
144 return FAIL;
145 if (ga_grow(&blob->bv_ga, st.st_size) == FAIL)
146 return FAIL;
147 blob->bv_ga.ga_len = st.st_size;
148 if (fread(blob->bv_ga.ga_data, 1, blob->bv_ga.ga_len, fd)
149 < (size_t)blob->bv_ga.ga_len)
150 return FAIL;
151 return OK;
152}
153
154/*
155 * Write "blob" to file "fd".
156 * Return OK or FAIL.
157 */
158 int
159write_blob(FILE *fd, blob_T *blob)
160{
161 if (fwrite(blob->bv_ga.ga_data, 1, blob->bv_ga.ga_len, fd)
162 < (size_t)blob->bv_ga.ga_len)
163 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100164 emsg(_(e_write));
Bram Moolenaar6e5ea8d2019-01-12 22:47:31 +0100165 return FAIL;
166 }
167 return OK;
168}
169
Bram Moolenaar8c8b8bb2019-01-13 17:48:04 +0100170/*
171 * Convert a blob to a readable form: "[0x11,0x34]"
172 */
173 char_u *
174blob2string(blob_T *blob, char_u **tofree, char_u *numbuf)
175{
176 int i;
177 garray_T ga;
178
179 if (blob == NULL)
180 {
181 *tofree = NULL;
182 return (char_u *)"[]";
183 }
184
185 // Store bytes in the growarray.
186 ga_init2(&ga, 1, 4000);
187 ga_append(&ga, '[');
188 for (i = 0; i < blob_len(blob); i++)
189 {
190 if (i > 0)
191 ga_concat(&ga, (char_u *)",");
192 vim_snprintf((char *)numbuf, NUMBUFLEN, "0x%02X", (int)blob_get(blob, i));
193 ga_concat(&ga, numbuf);
194 }
195 ga_append(&ga, ']');
196 *tofree = ga.ga_data;
197 return *tofree;
198}
199
200/*
201 * Convert a string variable, in the format of blob2string(), to a blob.
202 * Return NULL when conversion failed.
203 */
204 blob_T *
205string2blob(char_u *str)
206{
207 blob_T *blob = blob_alloc();
208 char_u *s = str;
209
210 if (*s != '[')
211 goto failed;
212 s = skipwhite(s + 1);
213 while (*s != ']')
214 {
215 if (s[0] != '0' || s[1] != 'x'
216 || !vim_isxdigit(s[2]) || !vim_isxdigit(s[3]))
217 goto failed;
218 ga_append(&blob->bv_ga, (hex2nr(s[2]) << 4) + hex2nr(s[3]));
219 s += 4;
220 if (*s == ',')
221 s = skipwhite(s + 1);
222 else if (*s != ']')
223 goto failed;
224 }
225 s = skipwhite(s + 1);
226 if (*s != NUL)
227 goto failed; // text after final ']'
228
229 ++blob->bv_refcount;
230 return blob;
231
232failed:
233 blob_free(blob);
234 return NULL;
235}
236
Bram Moolenaar6e5ea8d2019-01-12 22:47:31 +0100237#endif /* defined(FEAT_EVAL) */