bson_ext 1.3.1 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,229 @@
1
+ /* bson.h */
2
+
3
+ /* Copyright 2009, 2010 10gen Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #ifndef _BSON_H_
19
+ #define _BSON_H_
20
+
21
+ #include "platform_hacks.h"
22
+ #include <time.h>
23
+
24
+ MONGO_EXTERN_C_START
25
+
26
+ typedef enum {
27
+ bson_eoo=0 ,
28
+ bson_double=1,
29
+ bson_string=2,
30
+ bson_object=3,
31
+ bson_array=4,
32
+ bson_bindata=5,
33
+ bson_undefined=6,
34
+ bson_oid=7,
35
+ bson_bool=8,
36
+ bson_date=9,
37
+ bson_null=10,
38
+ bson_regex=11,
39
+ bson_dbref=12, /* deprecated */
40
+ bson_code=13,
41
+ bson_symbol=14,
42
+ bson_codewscope=15,
43
+ bson_int = 16,
44
+ bson_timestamp = 17,
45
+ bson_long = 18
46
+ } bson_type;
47
+
48
+ typedef int bson_bool_t;
49
+
50
+ typedef struct {
51
+ char * data;
52
+ bson_bool_t owned;
53
+ } bson;
54
+
55
+ typedef struct {
56
+ const char * cur;
57
+ bson_bool_t first;
58
+ } bson_iterator;
59
+
60
+ typedef struct {
61
+ char * buf;
62
+ char * cur;
63
+ int bufSize;
64
+ bson_bool_t finished;
65
+ int stack[32];
66
+ int stackPos;
67
+ } bson_buffer;
68
+
69
+ #pragma pack(1)
70
+ typedef union{
71
+ char bytes[12];
72
+ int ints[3];
73
+ } bson_oid_t;
74
+ #pragma pack()
75
+
76
+ typedef int64_t bson_date_t; /* milliseconds since epoch UTC */
77
+
78
+ typedef struct {
79
+ int i; /* increment */
80
+ int t; /* time in seconds */
81
+ } bson_timestamp_t;
82
+
83
+ /* ----------------------------
84
+ READING
85
+ ------------------------------ */
86
+
87
+ bson * bson_empty(bson * obj); /* returns pointer to static empty bson object */
88
+ void bson_copy(bson* out, const bson* in); /* puts data in new buffer. NOOP if out==NULL */
89
+ bson * bson_from_buffer(bson * b, bson_buffer * buf);
90
+ bson * bson_init( bson * b , char * data , bson_bool_t mine );
91
+ int bson_size(const bson * b );
92
+ void bson_destroy( bson * b );
93
+
94
+ void bson_print( bson * b );
95
+ void bson_print_raw( const char * bson , int depth );
96
+
97
+ /* advances iterator to named field */
98
+ /* returns bson_eoo (which is false) if field not found */
99
+ bson_type bson_find(bson_iterator* it, const bson* obj, const char* name);
100
+
101
+ void bson_iterator_init( bson_iterator * i , const char * bson );
102
+
103
+ /* more returns true for eoo. best to loop with bson_iterator_next(&it) */
104
+ bson_bool_t bson_iterator_more( const bson_iterator * i );
105
+ bson_type bson_iterator_next( bson_iterator * i );
106
+
107
+ bson_type bson_iterator_type( const bson_iterator * i );
108
+ const char * bson_iterator_key( const bson_iterator * i );
109
+ const char * bson_iterator_value( const bson_iterator * i );
110
+
111
+ /* these convert to the right type (return 0 if non-numeric) */
112
+ double bson_iterator_double( const bson_iterator * i );
113
+ int bson_iterator_int( const bson_iterator * i );
114
+ int64_t bson_iterator_long( const bson_iterator * i );
115
+
116
+ /* return the bson timestamp as a whole or in parts */
117
+ bson_timestamp_t bson_iterator_timestamp( const bson_iterator * i );
118
+
119
+ /* false: boolean false, 0 in any type, or null */
120
+ /* true: anything else (even empty strings and objects) */
121
+ bson_bool_t bson_iterator_bool( const bson_iterator * i );
122
+
123
+ /* these assume you are using the right type */
124
+ double bson_iterator_double_raw( const bson_iterator * i );
125
+ int bson_iterator_int_raw( const bson_iterator * i );
126
+ int64_t bson_iterator_long_raw( const bson_iterator * i );
127
+ bson_bool_t bson_iterator_bool_raw( const bson_iterator * i );
128
+ bson_oid_t* bson_iterator_oid( const bson_iterator * i );
129
+
130
+ /* these can also be used with bson_code and bson_symbol*/
131
+ const char * bson_iterator_string( const bson_iterator * i );
132
+ int bson_iterator_string_len( const bson_iterator * i );
133
+
134
+ /* works with bson_code, bson_codewscope, and bson_string */
135
+ /* returns NULL for everything else */
136
+ const char * bson_iterator_code(const bson_iterator * i);
137
+
138
+ /* calls bson_empty on scope if not a bson_codewscope */
139
+ void bson_iterator_code_scope(const bson_iterator * i, bson * scope);
140
+
141
+ /* both of these only work with bson_date */
142
+ bson_date_t bson_iterator_date(const bson_iterator * i);
143
+ time_t bson_iterator_time_t(const bson_iterator * i);
144
+
145
+ int bson_iterator_bin_len( const bson_iterator * i );
146
+ char bson_iterator_bin_type( const bson_iterator * i );
147
+ const char * bson_iterator_bin_data( const bson_iterator * i );
148
+
149
+ const char * bson_iterator_regex( const bson_iterator * i );
150
+ const char * bson_iterator_regex_opts( const bson_iterator * i );
151
+
152
+ /* these work with bson_object and bson_array */
153
+ void bson_iterator_subobject(const bson_iterator * i, bson * sub);
154
+ void bson_iterator_subiterator(const bson_iterator * i, bson_iterator * sub);
155
+
156
+ /* str must be at least 24 hex chars + null byte */
157
+ void bson_oid_from_string(bson_oid_t* oid, const char* str);
158
+ void bson_oid_to_string(const bson_oid_t* oid, char* str);
159
+ void bson_oid_gen(bson_oid_t* oid);
160
+
161
+ time_t bson_oid_generated_time(bson_oid_t* oid); /* Gives the time the OID was created */
162
+
163
+ /* ----------------------------
164
+ BUILDING
165
+ ------------------------------ */
166
+
167
+ bson_buffer * bson_buffer_init( bson_buffer * b );
168
+ bson_buffer * bson_ensure_space( bson_buffer * b , const int bytesNeeded );
169
+
170
+ /**
171
+ * @return the raw data. you either should free this OR call bson_destroy not both
172
+ */
173
+ char * bson_buffer_finish( bson_buffer * b );
174
+ void bson_buffer_destroy( bson_buffer * b );
175
+
176
+ bson_buffer * bson_append_oid( bson_buffer * b , const char * name , const bson_oid_t* oid );
177
+ bson_buffer * bson_append_new_oid( bson_buffer * b , const char * name );
178
+ bson_buffer * bson_append_int( bson_buffer * b , const char * name , const int i );
179
+ bson_buffer * bson_append_long( bson_buffer * b , const char * name , const int64_t i );
180
+ bson_buffer * bson_append_double( bson_buffer * b , const char * name , const double d );
181
+ bson_buffer * bson_append_string( bson_buffer * b , const char * name , const char * str );
182
+ bson_buffer * bson_append_string_n( bson_buffer * b, const char * name, const char * str , int len);
183
+ bson_buffer * bson_append_symbol( bson_buffer * b , const char * name , const char * str );
184
+ bson_buffer * bson_append_symbol_n( bson_buffer * b , const char * name , const char * str , int len );
185
+ bson_buffer * bson_append_code( bson_buffer * b , const char * name , const char * str );
186
+ bson_buffer * bson_append_code_n( bson_buffer * b , const char * name , const char * str , int len );
187
+ bson_buffer * bson_append_code_w_scope( bson_buffer * b , const char * name , const char * code , const bson * scope);
188
+ bson_buffer * bson_append_code_w_scope_n( bson_buffer * b , const char * name , const char * code , int size , const bson * scope);
189
+ bson_buffer * bson_append_binary( bson_buffer * b, const char * name, char type, const char * str, int len );
190
+ bson_buffer * bson_append_bool( bson_buffer * b , const char * name , const bson_bool_t v );
191
+ bson_buffer * bson_append_null( bson_buffer * b , const char * name );
192
+ bson_buffer * bson_append_undefined( bson_buffer * b , const char * name );
193
+ bson_buffer * bson_append_regex( bson_buffer * b , const char * name , const char * pattern, const char * opts );
194
+ bson_buffer * bson_append_bson( bson_buffer * b , const char * name , const bson* bson);
195
+ bson_buffer * bson_append_element( bson_buffer * b, const char * name_or_null, const bson_iterator* elem);
196
+ bson_buffer * bson_append_timestamp( bson_buffer * b, const char * name, bson_timestamp_t * ts );
197
+
198
+ /* these both append a bson_date */
199
+ bson_buffer * bson_append_date(bson_buffer * b, const char * name, bson_date_t millis);
200
+ bson_buffer * bson_append_time_t(bson_buffer * b, const char * name, time_t secs);
201
+
202
+ bson_buffer * bson_append_start_object( bson_buffer * b , const char * name );
203
+ bson_buffer * bson_append_start_array( bson_buffer * b , const char * name );
204
+ bson_buffer * bson_append_finish_object( bson_buffer * b );
205
+
206
+ void bson_numstr(char* str, int i);
207
+ void bson_incnumstr(char* str);
208
+
209
+
210
+ /* ------------------------------
211
+ ERROR HANDLING - also used in mongo code
212
+ ------------------------------ */
213
+
214
+ void * bson_malloc(int size); /* checks return value */
215
+ void * bson_realloc(void * ptr, int size); /* checks return value */
216
+
217
+ /* bson_err_handlers shouldn't return!!! */
218
+ typedef void(*bson_err_handler)(const char* errmsg);
219
+
220
+ /* returns old handler or NULL */
221
+ /* default handler prints error then exits with failure*/
222
+ bson_err_handler set_bson_err_handler(bson_err_handler func);
223
+
224
+ /* does nothing is ok != 0 */
225
+ void bson_fatal( int ok );
226
+ void bson_fatal_msg( int ok, const char* msg );
227
+
228
+ MONGO_EXTERN_C_END
229
+ #endif
@@ -0,0 +1,799 @@
1
+ /*--------------------------------------------------------------------*/
2
+ /* gridfs.c */
3
+ /* Author: Christopher Triolo */
4
+ /*--------------------------------------------------------------------*/
5
+
6
+ #include "gridfs.h"
7
+ #include "mongo.h"
8
+ #include "bson.h"
9
+ #include <stdio.h>
10
+ #include <stdlib.h>
11
+ #include <string.h>
12
+ #include <assert.h>
13
+ #define TRUE 1
14
+ #define FALSE 0
15
+
16
+
17
+ /*--------------------------------------------------------------------*/
18
+
19
+ static bson * chunk_new(bson_oid_t id, int chunkNumber,
20
+ const char * data, int len)
21
+
22
+ {
23
+ bson * b;
24
+ bson_buffer buf;
25
+
26
+ b = (bson *)bson_malloc(sizeof(bson));
27
+ if (b == NULL) return NULL;
28
+
29
+ bson_buffer_init(&buf);
30
+ bson_append_oid(&buf, "files_id", &id);
31
+ bson_append_int(&buf, "n", chunkNumber);
32
+ bson_append_binary(&buf, "data", 2, data, len);
33
+ bson_from_buffer(b, &buf);
34
+ return b;
35
+ }
36
+
37
+ /*--------------------------------------------------------------------*/
38
+
39
+ static void chunk_free(bson * oChunk)
40
+
41
+ {
42
+ bson_destroy(oChunk);
43
+ free(oChunk);
44
+ }
45
+
46
+ /*--------------------------------------------------------------------*/
47
+
48
+ int gridfs_init(mongo_connection * client, const char * dbname,
49
+ const char * prefix, gridfs* gfs)
50
+ {
51
+ int options;
52
+ bson_buffer bb;
53
+ bson b;
54
+ bson out;
55
+ bson_bool_t success;
56
+
57
+ gfs->client = client;
58
+
59
+ /* Allocate space to own the dbname */
60
+ gfs->dbname = (const char *)bson_malloc(strlen(dbname)+1);
61
+ if (gfs->dbname == NULL) {
62
+ return FALSE;
63
+ }
64
+ strcpy((char*)gfs->dbname, dbname);
65
+
66
+ /* Allocate space to own the prefix */
67
+ if (prefix == NULL) prefix = "fs";
68
+ gfs->prefix = (const char *)bson_malloc(strlen(prefix)+1);
69
+ if (gfs->prefix == NULL) {
70
+ free((char*)gfs->dbname);
71
+ return FALSE;
72
+ }
73
+ strcpy((char *)gfs->prefix, prefix);
74
+
75
+ /* Allocate space to own files_ns */
76
+ gfs->files_ns =
77
+ (const char *) bson_malloc (strlen(prefix)+strlen(dbname)+strlen(".files")+2);
78
+ if (gfs->files_ns == NULL) {
79
+ free((char*)gfs->dbname);
80
+ free((char*)gfs->prefix);
81
+ return FALSE;
82
+ }
83
+ strcpy((char*)gfs->files_ns, dbname);
84
+ strcat((char*)gfs->files_ns, ".");
85
+ strcat((char*)gfs->files_ns, prefix);
86
+ strcat((char*)gfs->files_ns, ".files");
87
+
88
+ /* Allocate space to own chunks_ns */
89
+ gfs->chunks_ns = (const char *) bson_malloc(strlen(prefix) + strlen(dbname)
90
+ + strlen(".chunks") + 2);
91
+ if (gfs->chunks_ns == NULL) {
92
+ free((char*)gfs->dbname);
93
+ free((char*)gfs->prefix);
94
+ free((char*)gfs->files_ns);
95
+ return FALSE;
96
+ }
97
+ strcpy((char*)gfs->chunks_ns, dbname);
98
+ strcat((char*)gfs->chunks_ns, ".");
99
+ strcat((char*)gfs->chunks_ns, prefix);
100
+ strcat((char*)gfs->chunks_ns, ".chunks");
101
+
102
+ bson_buffer_init(&bb);
103
+ bson_append_int(&bb, "filename", 1);
104
+ bson_from_buffer(&b, &bb);
105
+ options = 0;
106
+ success = mongo_create_index(gfs->client, gfs->files_ns, &b, options, &out);
107
+ bson_destroy(&b);
108
+ if (!success) {
109
+ free((char*)gfs->dbname);
110
+ free((char*)gfs->prefix);
111
+ free((char*)gfs->files_ns);
112
+ free((char*)gfs->chunks_ns);
113
+ return FALSE;
114
+ }
115
+
116
+ bson_buffer_init(&bb);
117
+ bson_append_int(&bb, "files_id", 1);
118
+ bson_append_int(&bb, "n", 1);
119
+ bson_from_buffer(&b, &bb);
120
+ options = MONGO_INDEX_UNIQUE;
121
+ success = mongo_create_index(gfs->client, gfs->chunks_ns, &b, options, &out);
122
+ bson_destroy(&b);
123
+ if (!success) {
124
+ free((char*)gfs->dbname);
125
+ free((char*)gfs->prefix);
126
+ free((char*)gfs->files_ns);
127
+ free((char*)gfs->chunks_ns);
128
+ return FALSE;
129
+ }
130
+
131
+ return TRUE;
132
+ }
133
+
134
+ /*--------------------------------------------------------------------*/
135
+
136
+ void gridfs_destroy(gridfs* gfs)
137
+
138
+ {
139
+ if (gfs == NULL) return;
140
+ if (gfs->dbname) free((char*)gfs->dbname);
141
+ if (gfs->prefix) free((char*)gfs->prefix);
142
+ if (gfs->files_ns) free((char*)gfs->files_ns);
143
+ if (gfs->chunks_ns) free((char*)gfs->chunks_ns);
144
+ }
145
+
146
+ /*--------------------------------------------------------------------*/
147
+
148
+ static bson gridfs_insert_file( gridfs* gfs, const char* name,
149
+ const bson_oid_t id, gridfs_offset length,
150
+ const char* contenttype)
151
+ {
152
+ bson command;
153
+ bson res;
154
+ bson ret;
155
+ bson_buffer buf;
156
+ bson_iterator it;
157
+
158
+ /* Check run md5 */
159
+ bson_buffer_init(&buf);
160
+ bson_append_oid(&buf, "filemd5", &id);
161
+ bson_append_string(&buf, "root", gfs->prefix);
162
+ bson_from_buffer(&command, &buf);
163
+ assert(mongo_run_command(gfs->client, gfs->dbname,
164
+ &command, &res));
165
+ bson_destroy(&command);
166
+
167
+ /* Create and insert BSON for file metadata */
168
+ bson_buffer_init(&buf);
169
+ bson_append_oid(&buf, "_id", &id);
170
+ if (name != NULL && *name != '\0') {
171
+ bson_append_string(&buf, "filename", name);
172
+ }
173
+ bson_append_long(&buf, "length", length);
174
+ bson_append_int(&buf, "chunkSize", DEFAULT_CHUNK_SIZE);
175
+ bson_append_date(&buf, "uploadDate", (bson_date_t)1000*time(NULL));
176
+ bson_find(&it, &res, "md5");
177
+ bson_append_string(&buf, "md5", bson_iterator_string(&it));
178
+ bson_destroy(&res);
179
+ if (contenttype != NULL && *contenttype != '\0') {
180
+ bson_append_string(&buf, "contentType", contenttype);
181
+ }
182
+ bson_from_buffer(&ret, &buf);
183
+ mongo_insert(gfs->client, gfs->files_ns, &ret);
184
+
185
+ return ret;
186
+ }
187
+
188
+ /*--------------------------------------------------------------------*/
189
+
190
+ bson gridfs_store_buffer( gridfs* gfs, const char* data,
191
+ gridfs_offset length, const char* remotename,
192
+ const char * contenttype)
193
+
194
+ {
195
+ char const * const end = data + length;
196
+ bson_oid_t id;
197
+ int chunkNumber = 0;
198
+ int chunkLen;
199
+ bson * oChunk;
200
+
201
+ /* Large files Assertion */
202
+ assert(length <= 0xffffffff);
203
+
204
+ /* Generate and append an oid*/
205
+ bson_oid_gen(&id);
206
+
207
+ /* Insert the file's data chunk by chunk */
208
+ while (data < end) {
209
+ chunkLen = DEFAULT_CHUNK_SIZE < (unsigned int)(end - data) ?
210
+ DEFAULT_CHUNK_SIZE : (unsigned int)(end - data);
211
+ oChunk = chunk_new( id, chunkNumber, data, chunkLen );
212
+ mongo_insert(gfs->client, gfs->chunks_ns, oChunk);
213
+ chunk_free(oChunk);
214
+ chunkNumber++;
215
+ data += chunkLen;
216
+ }
217
+
218
+ /* Inserts file's metadata */
219
+ return gridfs_insert_file(gfs, remotename, id, length, contenttype);
220
+ }
221
+
222
+ /*--------------------------------------------------------------------*/
223
+
224
+ void gridfile_writer_init( gridfile* gfile, gridfs* gfs,
225
+ const char* remote_name, const char* content_type )
226
+ {
227
+ gfile->gfs = gfs;
228
+
229
+ bson_oid_gen( &(gfile->id) );
230
+ gfile->chunk_num = 0;
231
+ gfile->length = 0;
232
+ gfile->pending_len = 0;
233
+ gfile->pending_data = NULL;
234
+
235
+ gfile->remote_name = (const char *)bson_malloc( strlen( remote_name ) + 1 );
236
+ strcpy( (char *)gfile->remote_name, remote_name );
237
+
238
+ gfile->content_type = (const char *)bson_malloc( strlen( content_type ) );
239
+ strcpy( (char *)gfile->content_type, content_type );
240
+ }
241
+
242
+ /*--------------------------------------------------------------------*/
243
+
244
+ void gridfile_write_buffer( gridfile* gfile, const char* data, gridfs_offset length )
245
+ {
246
+
247
+ int bytes_left = 0;
248
+ int data_partial_len = 0;
249
+ int chunks_to_write = 0;
250
+ char* buffer;
251
+ bson* oChunk;
252
+ gridfs_offset to_write = length + gfile->pending_len;
253
+
254
+ if ( to_write < DEFAULT_CHUNK_SIZE ) { /* Less than one chunk to write */
255
+ if( gfile->pending_data ) {
256
+ gfile->pending_data = (char *)bson_realloc((void *)gfile->pending_data, gfile->pending_len + to_write);
257
+ memcpy( gfile->pending_data + gfile->pending_len, data, length );
258
+ } else if (to_write > 0) {
259
+ gfile->pending_data = (char *)bson_malloc(to_write);
260
+ memcpy( gfile->pending_data, data, length );
261
+ }
262
+ gfile->pending_len += length;
263
+
264
+ } else { /* At least one chunk of data to write */
265
+
266
+ /* If there's a pending chunk to be written, we need to combine
267
+ * the buffer provided up to DEFAULT_CHUNK_SIZE.
268
+ */
269
+ if ( gfile->pending_len > 0 ) {
270
+ chunks_to_write = to_write / DEFAULT_CHUNK_SIZE;
271
+ bytes_left = to_write % DEFAULT_CHUNK_SIZE;
272
+
273
+ data_partial_len = DEFAULT_CHUNK_SIZE - gfile->pending_len;
274
+ buffer = (char *)bson_malloc( DEFAULT_CHUNK_SIZE );
275
+ memcpy(buffer, gfile->pending_data, gfile->pending_len);
276
+ memcpy(buffer + gfile->pending_len, data, data_partial_len);
277
+
278
+ oChunk = chunk_new(gfile->id, gfile->chunk_num, buffer, DEFAULT_CHUNK_SIZE);
279
+ mongo_insert(gfile->gfs->client, gfile->gfs->chunks_ns, oChunk);
280
+ chunk_free(oChunk);
281
+ gfile->chunk_num++;
282
+ gfile->length += DEFAULT_CHUNK_SIZE;
283
+ data += data_partial_len;
284
+
285
+ chunks_to_write--;
286
+
287
+ free(buffer);
288
+ }
289
+
290
+ while( chunks_to_write > 0 ) {
291
+ oChunk = chunk_new(gfile->id, gfile->chunk_num, data, DEFAULT_CHUNK_SIZE);
292
+ mongo_insert(gfile->gfs->client, gfile->gfs->chunks_ns, oChunk);
293
+ chunk_free(oChunk);
294
+ gfile->chunk_num++;
295
+ chunks_to_write--;
296
+ gfile->length += DEFAULT_CHUNK_SIZE;
297
+ data += DEFAULT_CHUNK_SIZE;
298
+ }
299
+
300
+ free(gfile->pending_data);
301
+
302
+ /* If there are any leftover bytes, store them as pending data. */
303
+ if( bytes_left == 0 )
304
+ gfile->pending_data = NULL;
305
+ else {
306
+ gfile->pending_data = (char *)bson_malloc( bytes_left );
307
+ memcpy( gfile->pending_data, data, bytes_left );
308
+ }
309
+
310
+ gfile->pending_len = bytes_left;
311
+ }
312
+ }
313
+
314
+ /*--------------------------------------------------------------------*/
315
+
316
+ bson gridfile_writer_done( gridfile* gfile )
317
+ {
318
+
319
+ /* write any remaining pending chunk data.
320
+ * pending data will always take up less than one chunk
321
+ */
322
+ bson* oChunk;
323
+ if( gfile->pending_data )
324
+ {
325
+ oChunk = chunk_new(gfile->id, gfile->chunk_num, gfile->pending_data, gfile->pending_len);
326
+ mongo_insert(gfile->gfs->client, gfile->gfs->chunks_ns, oChunk);
327
+ chunk_free(oChunk);
328
+ free(gfile->pending_data);
329
+ gfile->length += gfile->pending_len;
330
+ }
331
+
332
+ /* insert into files collection */
333
+ return gridfs_insert_file(gfile->gfs, gfile->remote_name, gfile->id,
334
+ gfile->length, gfile->content_type);
335
+ }
336
+
337
+ /*--------------------------------------------------------------------*/
338
+
339
+ bson gridfs_store_file(gridfs* gfs, const char* filename,
340
+ const char* remotename, const char* contenttype)
341
+ {
342
+ char buffer[DEFAULT_CHUNK_SIZE];
343
+ FILE * fd;
344
+ bson_oid_t id;
345
+ int chunkNumber = 0;
346
+ gridfs_offset length = 0;
347
+ gridfs_offset chunkLen = 0;
348
+ bson* oChunk;
349
+
350
+ /* Open the file and the correct stream */
351
+ if (strcmp(filename, "-") == 0) fd = stdin;
352
+ else fd = fopen(filename, "rb");
353
+ assert(fd != NULL); /* No such file */
354
+
355
+ /* Generate and append an oid*/
356
+ bson_oid_gen(&id);
357
+
358
+ /* Insert the file chunk by chunk */
359
+ chunkLen = fread(buffer, 1, DEFAULT_CHUNK_SIZE, fd);
360
+ do {
361
+ oChunk = chunk_new( id, chunkNumber, buffer, chunkLen );
362
+ mongo_insert(gfs->client, gfs->chunks_ns, oChunk);
363
+ chunk_free(oChunk);
364
+ length += chunkLen;
365
+ chunkNumber++;
366
+ chunkLen = fread(buffer, 1, DEFAULT_CHUNK_SIZE, fd);
367
+ } while (chunkLen != 0);
368
+
369
+ /* Close the file stream */
370
+ if (fd != stdin) fclose(fd);
371
+
372
+ /* Large files Assertion */
373
+ /* assert(length <= 0xffffffff); */
374
+
375
+ /* Optional Remote Name */
376
+ if (remotename == NULL || *remotename == '\0') {
377
+ remotename = filename; }
378
+
379
+ /* Inserts file's metadata */
380
+ return gridfs_insert_file(gfs, remotename, id, length, contenttype);
381
+ }
382
+
383
+ /*--------------------------------------------------------------------*/
384
+
385
+ void gridfs_remove_filename(gridfs* gfs, const char* filename )
386
+
387
+ {
388
+ bson query;
389
+ bson_buffer buf;
390
+ mongo_cursor* files;
391
+ bson file;
392
+ bson_iterator it;
393
+ bson_oid_t id;
394
+ bson b;
395
+
396
+ bson_buffer_init(&buf);
397
+ bson_append_string(&buf, "filename", filename);
398
+ bson_from_buffer(&query, &buf);
399
+ files = mongo_find(gfs->client, gfs->files_ns, &query, NULL, 0, 0, 0);
400
+ bson_destroy(&query);
401
+
402
+ /* Remove each file and it's chunks from files named filename */
403
+ while (mongo_cursor_next(files)) {
404
+ file = files->current;
405
+ bson_find(&it, &file, "_id");
406
+ id = *bson_iterator_oid(&it);
407
+
408
+ /* Remove the file with the specified id */
409
+ bson_buffer_init(&buf);
410
+ bson_append_oid(&buf, "_id", &id);
411
+ bson_from_buffer(&b, &buf);
412
+ mongo_remove( gfs->client, gfs->files_ns, &b);
413
+ bson_destroy(&b);
414
+
415
+ /* Remove all chunks from the file with the specified id */
416
+ bson_buffer_init(&buf);
417
+ bson_append_oid(&buf, "files_id", &id);
418
+ bson_from_buffer(&b, &buf);
419
+ mongo_remove( gfs->client, gfs->chunks_ns, &b);
420
+ bson_destroy(&b);
421
+ }
422
+
423
+ }
424
+
425
+ /*--------------------------------------------------------------------*/
426
+
427
+ int gridfs_find_query(gridfs* gfs, bson* query,
428
+ gridfile* gfile )
429
+
430
+ {
431
+ bson_buffer date_buffer;
432
+ bson uploadDate;
433
+ bson_buffer buf;
434
+ bson finalQuery;
435
+ bson out;
436
+ int i;
437
+
438
+ bson_buffer_init(&date_buffer);
439
+ bson_append_int(&date_buffer, "uploadDate", -1);
440
+ bson_from_buffer(&uploadDate, &date_buffer);
441
+ bson_buffer_init(&buf);
442
+ bson_append_bson(&buf, "query", query);
443
+ bson_append_bson(&buf, "orderby", &uploadDate);
444
+ bson_from_buffer(&finalQuery, &buf);
445
+
446
+
447
+ i = (mongo_find_one(gfs->client, gfs->files_ns,
448
+ &finalQuery, NULL, &out));
449
+ bson_destroy(&uploadDate);
450
+ bson_destroy(&finalQuery);
451
+ if (!i)
452
+ return FALSE;
453
+ else {
454
+ gridfile_init(gfs, &out, gfile);
455
+ bson_destroy(&out);
456
+ return TRUE;
457
+ }
458
+ }
459
+
460
+ /*--------------------------------------------------------------------*/
461
+
462
+ int gridfs_find_filename(gridfs* gfs, const char* filename,
463
+ gridfile* gfile)
464
+
465
+ {
466
+ bson query;
467
+ bson_buffer buf;
468
+ int i;
469
+
470
+ bson_buffer_init(&buf);
471
+ bson_append_string(&buf, "filename", filename);
472
+ bson_from_buffer(&query, &buf) ;
473
+ i = gridfs_find_query(gfs, &query, gfile);
474
+ bson_destroy(&query);
475
+ return i;
476
+ }
477
+
478
+ /*--------------------------------------------------------------------*/
479
+
480
+ int gridfile_init(gridfs* gfs, bson* meta, gridfile* gfile)
481
+
482
+ {
483
+ gfile->gfs = gfs;
484
+ gfile->pos = 0;
485
+ gfile->meta = (bson*)bson_malloc(sizeof(bson));
486
+ if (gfile->meta == NULL) return FALSE;
487
+ bson_copy(gfile->meta, meta);
488
+ return TRUE;
489
+ }
490
+
491
+ /*--------------------------------------------------------------------*/
492
+
493
+ void gridfile_destroy(gridfile* gfile)
494
+
495
+ {
496
+ bson_destroy(gfile->meta);
497
+ free(gfile->meta);
498
+ }
499
+
500
+ /*--------------------------------------------------------------------*/
501
+
502
+ bson_bool_t gridfile_exists(gridfile* gfile)
503
+
504
+ {
505
+ return (bson_bool_t)(gfile != NULL || gfile->meta == NULL);
506
+ }
507
+
508
+ /*--------------------------------------------------------------------*/
509
+
510
+ const char* gridfile_get_filename(gridfile* gfile)
511
+
512
+ {
513
+ bson_iterator it;
514
+
515
+ bson_find(&it, gfile->meta, "filename");
516
+ return bson_iterator_string(&it);
517
+ }
518
+
519
+ /*--------------------------------------------------------------------*/
520
+
521
+ int gridfile_get_chunksize(gridfile* gfile)
522
+
523
+ {
524
+ bson_iterator it;
525
+
526
+ bson_find(&it, gfile->meta, "chunkSize");
527
+ return bson_iterator_int(&it);
528
+ }
529
+
530
+ /*--------------------------------------------------------------------*/
531
+
532
+ gridfs_offset gridfile_get_contentlength(gridfile* gfile)
533
+
534
+ {
535
+ bson_iterator it;
536
+
537
+ bson_find(&it, gfile->meta, "length");
538
+
539
+ if( bson_iterator_type( &it ) == bson_int )
540
+ return (gridfs_offset)bson_iterator_int( &it );
541
+ else
542
+ return (gridfs_offset)bson_iterator_long( &it );
543
+ }
544
+
545
+ /*--------------------------------------------------------------------*/
546
+
547
+ const char *gridfile_get_contenttype(gridfile* gfile)
548
+
549
+ {
550
+ bson_iterator it;
551
+
552
+ if (bson_find(&it, gfile->meta, "contentType"))
553
+ return bson_iterator_string( &it );
554
+ else return NULL;
555
+ }
556
+
557
+ /*--------------------------------------------------------------------*/
558
+
559
+ bson_date_t gridfile_get_uploaddate(gridfile* gfile)
560
+
561
+ {
562
+ bson_iterator it;
563
+
564
+ bson_find(&it, gfile->meta, "uploadDate");
565
+ return bson_iterator_date( &it );
566
+ }
567
+
568
+ /*--------------------------------------------------------------------*/
569
+
570
+ const char* gridfile_get_md5(gridfile* gfile)
571
+
572
+ {
573
+ bson_iterator it;
574
+
575
+ bson_find(&it, gfile->meta, "md5");
576
+ return bson_iterator_string( &it );
577
+ }
578
+
579
+ /*--------------------------------------------------------------------*/
580
+
581
+ const char* gridfile_get_field(gridfile* gfile, const char* name)
582
+
583
+ {
584
+ bson_iterator it;
585
+
586
+ bson_find(&it, gfile->meta, name);
587
+ return bson_iterator_value( &it );
588
+ }
589
+
590
+ /*--------------------------------------------------------------------*/
591
+
592
+ bson_bool_t gridfile_get_boolean(gridfile* gfile, const char* name)
593
+ {
594
+ bson_iterator it;
595
+
596
+ bson_find(&it, gfile->meta, name);
597
+ return bson_iterator_bool( &it );
598
+ }
599
+
600
+ /*--------------------------------------------------------------------*/
601
+ bson gridfile_get_metadata(gridfile* gfile)
602
+
603
+ {
604
+ bson sub;
605
+ bson_iterator it;
606
+
607
+ if (bson_find(&it, gfile->meta, "metadata")) {
608
+ bson_iterator_subobject( &it, &sub );
609
+ return sub;
610
+ }
611
+ else {
612
+ bson_empty(&sub);
613
+ return sub;
614
+ }
615
+ }
616
+
617
+ /*--------------------------------------------------------------------*/
618
+
619
+ int gridfile_get_numchunks(gridfile* gfile)
620
+
621
+ {
622
+ bson_iterator it;
623
+ gridfs_offset length;
624
+ gridfs_offset chunkSize;
625
+ double numchunks;
626
+
627
+ bson_find(&it, gfile->meta, "length");
628
+
629
+ if( bson_iterator_type( &it ) == bson_int )
630
+ length = (gridfs_offset)bson_iterator_int( &it );
631
+ else
632
+ length = (gridfs_offset)bson_iterator_long( &it );
633
+
634
+ bson_find(&it, gfile->meta, "chunkSize");
635
+ chunkSize = bson_iterator_int(&it);
636
+ numchunks = ((double)length/(double)chunkSize);
637
+ return (numchunks - (int)numchunks > 0)
638
+ ? (int)(numchunks+1)
639
+ : (int)(numchunks);
640
+ }
641
+
642
+ /*--------------------------------------------------------------------*/
643
+
644
+ bson gridfile_get_chunk(gridfile* gfile, int n)
645
+
646
+ {
647
+ bson query;
648
+ bson out;
649
+ bson_buffer buf;
650
+ bson_iterator it;
651
+ bson_oid_t id;
652
+
653
+ bson_buffer_init(&buf);
654
+ bson_find(&it, gfile->meta, "_id");
655
+ id = *bson_iterator_oid(&it);
656
+ bson_append_oid(&buf, "files_id", &id);
657
+ bson_append_int(&buf, "n", n);
658
+ bson_from_buffer(&query, &buf);
659
+
660
+ assert(mongo_find_one(gfile->gfs->client,
661
+ gfile->gfs->chunks_ns,
662
+ &query, NULL, &out));
663
+ return out;
664
+ }
665
+
666
+ /*--------------------------------------------------------------------*/
667
+
668
+ mongo_cursor* gridfile_get_chunks(gridfile* gfile, int start, int size)
669
+
670
+ {
671
+ bson_iterator it;
672
+ bson_oid_t id;
673
+ bson_buffer gte_buf;
674
+ bson gte_bson;
675
+ bson_buffer query_buf;
676
+ bson query_bson;
677
+ bson_buffer orderby_buf;
678
+ bson orderby_bson;
679
+ bson_buffer command_buf;
680
+ bson command_bson;
681
+
682
+ bson_find(&it, gfile->meta, "_id");
683
+ id = *bson_iterator_oid(&it);
684
+
685
+ bson_buffer_init(&query_buf);
686
+ bson_append_oid(&query_buf, "files_id", &id);
687
+ if (size == 1) {
688
+ bson_append_int(&query_buf, "n", start);
689
+ } else {
690
+ bson_buffer_init(&gte_buf);
691
+ bson_append_int(&gte_buf, "$gte", start);
692
+ bson_from_buffer(&gte_bson, &gte_buf);
693
+ bson_append_bson(&query_buf, "n", &gte_bson);
694
+ }
695
+ bson_from_buffer(&query_bson, &query_buf);
696
+
697
+ bson_buffer_init(&orderby_buf);
698
+ bson_append_int(&orderby_buf, "n", 1);
699
+ bson_from_buffer(&orderby_bson, &orderby_buf);
700
+
701
+ bson_buffer_init(&command_buf);
702
+ bson_append_bson(&command_buf, "query", &query_bson);
703
+ bson_append_bson(&command_buf, "orderby", &orderby_bson);
704
+ bson_from_buffer(&command_bson, &command_buf);
705
+
706
+ return mongo_find(gfile->gfs->client, gfile->gfs->chunks_ns,
707
+ &command_bson, NULL, size, 0, 0);
708
+ }
709
+
710
+ /*--------------------------------------------------------------------*/
711
+
712
+ gridfs_offset gridfile_write_file(gridfile* gfile, FILE *stream)
713
+
714
+ {
715
+ int i;
716
+ size_t len;
717
+ bson chunk;
718
+ bson_iterator it;
719
+ const char* data;
720
+ const int num = gridfile_get_numchunks( gfile );
721
+
722
+ for ( i=0; i<num; i++ ){
723
+ chunk = gridfile_get_chunk( gfile, i );
724
+ bson_find( &it, &chunk, "data" );
725
+ len = bson_iterator_bin_len( &it );
726
+ data = bson_iterator_bin_data( &it );
727
+ fwrite( data , sizeof(char), len, stream );
728
+ }
729
+
730
+ return gridfile_get_contentlength(gfile);
731
+ }
732
+
733
+ /*--------------------------------------------------------------------*/
734
+
735
+ gridfs_offset gridfile_read(gridfile* gfile, gridfs_offset size, char* buf)
736
+
737
+ {
738
+ mongo_cursor* chunks;
739
+ bson chunk;
740
+
741
+ int first_chunk;
742
+ int last_chunk;
743
+ int total_chunks;
744
+ gridfs_offset chunksize;
745
+ gridfs_offset contentlength;
746
+ gridfs_offset bytes_left;
747
+ int i;
748
+ bson_iterator it;
749
+ gridfs_offset chunk_len;
750
+ const char * chunk_data;
751
+
752
+ contentlength = gridfile_get_contentlength(gfile);
753
+ chunksize = gridfile_get_chunksize(gfile);
754
+ size = (contentlength - gfile->pos < size)
755
+ ? contentlength - gfile->pos
756
+ : size;
757
+ bytes_left = size;
758
+
759
+ first_chunk = (gfile->pos)/chunksize;
760
+ last_chunk = (gfile->pos+size-1)/chunksize;
761
+ total_chunks = last_chunk - first_chunk + 1;
762
+ chunks = gridfile_get_chunks(gfile, first_chunk, total_chunks);
763
+
764
+ for (i = 0; i < total_chunks; i++) {
765
+ mongo_cursor_next(chunks);
766
+ chunk = chunks->current;
767
+ bson_find(&it, &chunk, "data");
768
+ chunk_len = bson_iterator_bin_len( &it );
769
+ chunk_data = bson_iterator_bin_data( &it );
770
+ if (i == 0) {
771
+ chunk_data += (gfile->pos)%chunksize;
772
+ chunk_len -= (gfile->pos)%chunksize;
773
+ }
774
+ if (bytes_left > chunk_len) {
775
+ memcpy(buf, chunk_data, chunk_len);
776
+ bytes_left -= chunk_len;
777
+ buf += chunk_len;
778
+ } else {
779
+ memcpy(buf, chunk_data, bytes_left);
780
+ }
781
+ }
782
+
783
+ mongo_cursor_destroy(chunks);
784
+ gfile->pos = gfile->pos + size;
785
+
786
+ return size;
787
+ }
788
+
789
+ /*--------------------------------------------------------------------*/
790
+
791
+ gridfs_offset gridfile_seek(gridfile* gfile, gridfs_offset offset)
792
+
793
+ {
794
+ gridfs_offset length;
795
+
796
+ length = gridfile_get_contentlength(gfile);
797
+ gfile->pos = length < offset ? length : offset;
798
+ return gfile->pos;
799
+ }