1 /*
2  * Copyright 2013 MongoDB Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 
18 #undef MONGOC_LOG_DOMAIN
19 #define MONGOC_LOG_DOMAIN "gridfs_file_page"
20 
21 #include "mongoc-gridfs-file-page.h"
22 #include "mongoc-gridfs-file-page-private.h"
23 
24 #include "mongoc-trace-private.h"
25 
26 
27 /** create a new page from a buffer
28  *
29  * The buffer should stick around for the life of the page
30  */
31 mongoc_gridfs_file_page_t *
_mongoc_gridfs_file_page_new(const uint8_t * data,uint32_t len,uint32_t chunk_size)32 _mongoc_gridfs_file_page_new (const uint8_t *data,
33                               uint32_t len,
34                               uint32_t chunk_size)
35 {
36    mongoc_gridfs_file_page_t *page;
37 
38    ENTRY;
39 
40    BSON_ASSERT (data);
41    BSON_ASSERT (len <= chunk_size);
42 
43    page = (mongoc_gridfs_file_page_t *) bson_malloc0 (sizeof *page);
44 
45    page->chunk_size = chunk_size;
46    page->read_buf = data;
47    page->len = len;
48 
49    RETURN (page);
50 }
51 
52 
53 bool
_mongoc_gridfs_file_page_seek(mongoc_gridfs_file_page_t * page,uint32_t offset)54 _mongoc_gridfs_file_page_seek (mongoc_gridfs_file_page_t *page, uint32_t offset)
55 {
56    ENTRY;
57 
58    BSON_ASSERT (page);
59 
60    page->offset = offset;
61 
62    RETURN (1);
63 }
64 
65 
66 int32_t
_mongoc_gridfs_file_page_read(mongoc_gridfs_file_page_t * page,void * dst,uint32_t len)67 _mongoc_gridfs_file_page_read (mongoc_gridfs_file_page_t *page,
68                                void *dst,
69                                uint32_t len)
70 {
71    int bytes_read;
72    const uint8_t *src;
73 
74    ENTRY;
75 
76    BSON_ASSERT (page);
77    BSON_ASSERT (dst);
78 
79    bytes_read = BSON_MIN (len, page->len - page->offset);
80 
81    src = page->read_buf ? page->read_buf : page->buf;
82 
83    memcpy (dst, src + page->offset, bytes_read);
84 
85    page->offset += bytes_read;
86 
87    RETURN (bytes_read);
88 }
89 
90 
91 /**
92  * _mongoc_gridfs_file_page_write:
93  *
94  * Write to a page.
95  *
96 * Writes are copy-on-write with regards to the buffer that was passed to the
97  * mongoc_gridfs_file_page_t during construction. In other words, the first
98  * write allocates a large enough buffer for file->chunk_size, which becomes
99  * authoritative from then on.
100  *
101  * A write of zero bytes will trigger the copy-on-write mechanism.
102  */
103 int32_t
_mongoc_gridfs_file_page_write(mongoc_gridfs_file_page_t * page,const void * src,uint32_t len)104 _mongoc_gridfs_file_page_write (mongoc_gridfs_file_page_t *page,
105                                 const void *src,
106                                 uint32_t len)
107 {
108    int bytes_written;
109 
110    ENTRY;
111 
112    BSON_ASSERT (page);
113    BSON_ASSERT (src);
114 
115    bytes_written = BSON_MIN (len, page->chunk_size - page->offset);
116 
117    if (!page->buf) {
118       page->buf = (uint8_t *) bson_malloc (page->chunk_size);
119       memcpy (
120          page->buf, page->read_buf, BSON_MIN (page->chunk_size, page->len));
121    }
122 
123    /* Copy bytes and adjust the page position */
124    memcpy (page->buf + page->offset, src, bytes_written);
125    page->offset += bytes_written;
126    page->len = BSON_MAX (page->offset, page->len);
127 
128    /* Don't use the old read buffer, which is no longer current */
129    page->read_buf = page->buf;
130 
131    RETURN (bytes_written);
132 }
133 
134 
135 /**
136  * _mongoc_gridfs_file_page_memset0:
137  *
138  *      Write zeros to a page, starting from the page's current position. Up to
139  *      `len` bytes will be set to zero or until the page is full, whichever
140  *      comes first.
141  *
142  *      Like _mongoc_gridfs_file_page_write, operations are copy-on-write with
143  *      regards to the page buffer.
144  *
145  * Returns:
146  *      Number of bytes set.
147  */
148 uint32_t
_mongoc_gridfs_file_page_memset0(mongoc_gridfs_file_page_t * page,uint32_t len)149 _mongoc_gridfs_file_page_memset0 (mongoc_gridfs_file_page_t *page, uint32_t len)
150 {
151    uint32_t bytes_set;
152 
153    ENTRY;
154 
155    BSON_ASSERT (page);
156 
157    bytes_set = BSON_MIN (page->chunk_size - page->offset, len);
158 
159    if (!page->buf) {
160       page->buf = (uint8_t *) bson_malloc0 (page->chunk_size);
161       memcpy (
162          page->buf, page->read_buf, BSON_MIN (page->chunk_size, page->len));
163    }
164 
165    /* Set bytes and adjust the page position */
166    memset (page->buf + page->offset, '\0', bytes_set);
167    page->offset += bytes_set;
168    page->len = BSON_MAX (page->offset, page->len);
169 
170    /* Don't use the old read buffer, which is no longer current */
171    page->read_buf = page->buf;
172 
173    RETURN (bytes_set);
174 }
175 
176 
177 const uint8_t *
_mongoc_gridfs_file_page_get_data(mongoc_gridfs_file_page_t * page)178 _mongoc_gridfs_file_page_get_data (mongoc_gridfs_file_page_t *page)
179 {
180    ENTRY;
181 
182    BSON_ASSERT (page);
183 
184    RETURN (page->buf ? page->buf : page->read_buf);
185 }
186 
187 
188 uint32_t
_mongoc_gridfs_file_page_get_len(mongoc_gridfs_file_page_t * page)189 _mongoc_gridfs_file_page_get_len (mongoc_gridfs_file_page_t *page)
190 {
191    ENTRY;
192 
193    BSON_ASSERT (page);
194 
195    RETURN (page->len);
196 }
197 
198 
199 uint32_t
_mongoc_gridfs_file_page_tell(mongoc_gridfs_file_page_t * page)200 _mongoc_gridfs_file_page_tell (mongoc_gridfs_file_page_t *page)
201 {
202    ENTRY;
203 
204    BSON_ASSERT (page);
205 
206    RETURN (page->offset);
207 }
208 
209 
210 bool
_mongoc_gridfs_file_page_is_dirty(mongoc_gridfs_file_page_t * page)211 _mongoc_gridfs_file_page_is_dirty (mongoc_gridfs_file_page_t *page)
212 {
213    ENTRY;
214 
215    BSON_ASSERT (page);
216 
217    RETURN (page->buf ? 1 : 0);
218 }
219 
220 
221 void
_mongoc_gridfs_file_page_destroy(mongoc_gridfs_file_page_t * page)222 _mongoc_gridfs_file_page_destroy (mongoc_gridfs_file_page_t *page)
223 {
224    ENTRY;
225 
226    if (page->buf) {
227       bson_free (page->buf);
228    }
229 
230    bson_free (page);
231 
232    EXIT;
233 }
234