bson_ext 1.3.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+ }