OSDN Git Service

Add MS7619SE
[uclinux-h8/uClinux-dist.git] / user / netflash / fileblock_ram.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include "fileblock.h"
5
6 struct fileblock {
7         void *data;
8         unsigned long length;
9         unsigned long maxlength;
10         struct fileblock *next;
11 };
12
13 static unsigned long file_length;
14
15 static struct fileblock *first;
16 static unsigned long first_pos;
17
18 static struct fileblock *current;
19 static unsigned long current_pos;       /* includes current_offset */
20 static unsigned long current_offset;
21
22 static struct fileblock *thrown;
23
24 #define BLOCK_OVERHEAD  16
25 #define block_len       (_block_len - BLOCK_OVERHEAD)
26 int _block_len = 8192;
27
28 #if 0
29 #define debug fprintf
30 #else
31 #define debug(...) do { } while (0)
32 #endif
33
34 unsigned long fb_len()
35 {
36         return file_length;
37 }
38
39 int fb_seek_set(unsigned long pos)
40 {
41         debug(stderr, "fb_seek_set(%ld), first %ld/%ld\n", pos, first_pos, file_length);
42
43         if (pos < first_pos || pos >= file_length)
44                 return -1;
45
46         current = first;
47         current_pos = first_pos;
48         current_offset = 0;
49         return fb_seek_inc(pos - current_pos);
50 }
51
52 int fb_seek_end(unsigned long offset)
53 {
54         debug(stderr, "fb_seek_end(%ld)\n", offset);
55
56         if (offset > file_length)
57                 return -1;
58
59         return fb_seek_set(file_length - offset);
60 }
61
62 int fb_seek_inc(unsigned long offset)
63 {
64         unsigned long l;
65
66         debug(stderr, "fb_seek_inc(%ld), current %ld/%ld\n", offset, current_pos, file_length);
67
68         if (offset >= file_length - current_pos)
69                 return -1;
70
71         while ((l = current->length - current_offset) < offset) {
72                 offset -= l;
73                 current_pos += l;
74                 current_offset = 0;
75                 current = current->next;
76         }
77
78         current_pos += offset;
79         current_offset += offset;
80         return 0;
81 }
82
83 int fb_seek_dec(unsigned long offset)
84 {
85         debug(stderr, "fb_seek_dec(%ld), current %ld\n", offset, current_pos);
86
87         if (offset > current_pos)
88                 return -1;
89
90         if (offset <= current_offset) {
91                 current_pos -= offset;
92                 current_offset -= offset;
93                 return 0;
94         }
95
96         return fb_seek_set(current_pos - offset);
97 }
98
99 unsigned long fb_tell(void)
100 {
101         return current_pos;
102 }
103
104 void fb_throw(unsigned long maxlen, void (* f)(void *, unsigned long))
105 {
106         struct fileblock *fb;
107
108         if (!first)
109                 return;
110
111         while (file_length - first_pos - first->length > maxlen) {
112                 debug(stderr, "fb_throw(%ld), first %ld/%ld, length %ld\n", maxlen, first_pos, first->length, file_length);
113
114                 fb = first;
115                 first = first->next;
116
117                 fb->next = thrown;
118                 thrown = fb;
119
120                 first_pos += fb->length;
121                 if (current_pos < first_pos) {
122                         current = first;
123                         current_pos = first_pos;
124                 }
125
126                 f(fb->data, fb->length);
127         }
128 }
129
130 static struct fileblock *fb_alloc(void)
131 {
132         struct fileblock *fb;
133
134         if (thrown) {
135                 fb = thrown;
136                 thrown = thrown->next;
137                 fb->length = 0;
138                 return fb;
139         }
140
141         fb = malloc(sizeof(*fb));
142         if (!fb)
143                 return NULL;
144
145         for (;;) {
146                 fb->data = malloc(block_len);
147                 if (fb->data)
148                         break;
149
150                 /* Halve the block size and try again, down to 1 page */
151                 if (_block_len < 4096) {
152                         free(fb);
153                         return NULL;
154                 }
155                 _block_len /= 2;
156         }
157
158         fb->next = NULL;
159         fb->length = 0;
160         fb->maxlength = block_len;
161         return fb;
162 }
163
164 int fb_write(const void *data, unsigned long len)
165 {
166         unsigned long l;
167         void *p;
168
169         debug(stderr, "fb_write(%ld), current %ld\n", len, current_pos);
170
171         if (!first) {
172                 first = fb_alloc();
173                 if (!first)
174                         return -1;
175                 current = first;
176         }
177
178         for (;;) {
179                 p = current->data + current_offset;
180                 l = current->maxlength - current_offset;
181                 if (l > len)
182                         l = len;
183
184                 memcpy(p, data, l);
185                 data += l;
186                 len -= l;
187
188                 current_pos += l;
189                 if (file_length < current_pos)
190                         file_length = current_pos;
191
192                 current_offset += l;
193                 if (current->length < current_offset)
194                         current->length = current_offset;
195
196                 if (len == 0)
197                         return 0;
198
199                 if (!current->next) {
200                         current->next = fb_alloc();
201                         if (!current->next)
202                                 return -1;
203                 }
204
205                 current = current->next;
206                 current_offset = 0;
207         }
208 }
209
210 int fb_peek(void *data, unsigned long len)
211 {
212         struct fileblock *fb;
213         unsigned long fb_pos;
214         unsigned long fb_offset;
215         int ret;
216
217         fb = current;
218         fb_pos = current_pos;
219         fb_offset = current_offset;
220         ret = fb_read(data, len);
221         current = fb;
222         current_pos = fb_pos;
223         current_offset = fb_offset;
224         return ret;
225 }
226
227 int fb_read(void *data, unsigned long len)
228 {
229         unsigned long readlen;
230         unsigned long l;
231         void *p;
232
233         debug(stderr, "fb_read(%ld), current %ld\n", len, current_pos);
234
235         if (file_length - current_pos < len)
236                 len = file_length - current_pos;
237
238         readlen = len;
239         for (;;) {
240                 p = current->data + current_offset;
241                 l = current->length - current_offset;
242                 if (l > len)
243                         l = len;
244
245                 memcpy(data, p, l);
246                 data += l;
247                 len -= l;
248
249                 current_pos += l;
250                 current_offset += l;
251
252                 if (len == 0)
253                         return readlen;
254
255                 current = current->next;
256                 current_offset = 0;
257         }
258 }
259
260 void *fb_read_block(unsigned long *len)
261 {
262         unsigned long l;
263         void *p;
264
265         debug(stderr, "fb_read_block(), current %ld/%ld\n", current_pos, current->length);
266
267         if (current_pos >= file_length)
268                 return NULL;
269
270         if (current_offset >= current->length) {
271                 current = current->next;
272                 current_offset = 0;
273         }
274
275         p = current->data + current_offset;
276         l = current->length - current_offset;
277
278         current_pos += l;
279         current_offset += l;
280
281         *len = l;
282         return p;
283 }
284
285 int fb_trim(unsigned long len)
286 {
287         struct fileblock *fb;
288         struct fileblock *fbnext;
289         unsigned long fb_pos;
290
291         debug(stderr, "fb_trim(%ld), length %ld\n", len, file_length);
292
293         if (len > file_length - first_pos)
294                 return -1;
295
296         file_length -= len;
297         for (fb = first, fb_pos = first_pos;
298                         fb_pos + fb->length < file_length;
299                         fb_pos += fb->length, fb = fb->next);
300
301         fb->length = file_length - fb_pos;
302
303         while (fb->next) {
304                 fbnext = fb->next;
305                 fb->next = fbnext->next;
306                 free(fbnext->data);
307                 free(fbnext);
308         }
309
310         if (current_pos > file_length)
311                 fb_seek_set(file_length);
312
313         return 0;
314 }