PageRenderTime 45ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/hphp/runtime/ext_zend_compat/mongo/gridfs/gridfs_stream.cpp

https://gitlab.com/Blueprint-Marketing/hhvm
C++ | 368 lines | 233 code | 78 blank | 57 comment | 34 complexity | d4896441a3f31a7134a39dce8c4f1752 MD5 | raw file
  1. /**
  2. * Copyright 2009-2013 10gen, 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. * Author: César D. Rodas <crodas@php.net>
  17. */
  18. #include <php.h>
  19. #ifdef WIN32
  20. # ifndef int64_t
  21. typedef __int64 int64_t;
  22. # endif
  23. #endif
  24. #include <php_globals.h>
  25. #include <ext/standard/file.h>
  26. #include <ext/standard/flock_compat.h>
  27. #ifdef HAVE_SYS_FILE_H
  28. # include <sys/file.h>
  29. #endif
  30. #include <zend_exceptions.h>
  31. #include "../php_mongo.h"
  32. #include "gridfs.h"
  33. #include "gridfs_stream.h"
  34. #include "../collection.h"
  35. #include "../cursor.h"
  36. #include "../db.h"
  37. extern zend_class_entry *mongo_ce_BinData, *mongo_ce_GridFS;
  38. extern zend_class_entry *mongo_ce_GridFSFile, *mongo_ce_GridFSException;
  39. ZEND_EXTERN_MODULE_GLOBALS(mongo)
  40. static size_t gridfs_read(php_stream *stream, char *buf, size_t count TSRMLS_DC);
  41. static int gridfs_close(php_stream *stream, int close_handle TSRMLS_DC);
  42. static int gridfs_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC);
  43. static int gridfs_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC);
  44. static int gridfs_seek(php_stream *stream, off_t offset, int whence, off_t *newoffs TSRMLS_DC);
  45. typedef struct _gridfs_stream_data {
  46. zval * fileObj; /* MongoGridFSFile Object */
  47. zval * chunkObj; /* Chunk collection object */
  48. zval * id; /* File ID */
  49. zval * query; /* Query array */
  50. /* file current position */
  51. size_t offset;
  52. /* file size */
  53. int size;
  54. /* chunk size */
  55. int chunkSize;
  56. int totalChunks;
  57. /* which chunk is loaded? */
  58. int chunkId;
  59. /* mongo current chunk is kept in memory */
  60. unsigned char * buffer;
  61. /* chunk size */
  62. int buffer_size;
  63. /* where we are in the chunk? */
  64. size_t buffer_offset;
  65. } gridfs_stream_data;
  66. php_stream_ops gridfs_stream_ops = {
  67. NULL, /* write */
  68. gridfs_read, /* read */
  69. gridfs_close, /* close */
  70. NULL, /* flush */
  71. "gridfs-wrapper",
  72. gridfs_seek, /* seek */
  73. NULL, /* cast */
  74. gridfs_stat, /* stat */
  75. gridfs_option, /* set_option */
  76. };
  77. /* some handy macros {{{ */
  78. #ifndef MIN
  79. # define MIN(a, b) a > b ? b : a
  80. #endif
  81. #if 0
  82. # define PRINTF_DEBUG(x) printf x;fflush(stdout);
  83. #else
  84. # define PRINTF_DEBUG(x)
  85. #endif
  86. /* returns 0 on failure */
  87. #define READ_ARRAY_PROP_PTR(dest, name, toVariable) \
  88. if (zend_hash_find(HASH_P(dest), name, strlen(name) + 1, (void**)&toVariable) == FAILURE) { \
  89. zend_throw_exception(mongo_ce_GridFSException, "couldn't find " name, 19 TSRMLS_CC); \
  90. return 0; \
  91. }
  92. /* returns FAILURE on failure */
  93. #define READ_ARRAY_PROP(dest, name, toVariable) \
  94. if (zend_hash_find(HASH_P(dest), name, strlen(name) + 1, (void**)&toVariable) == FAILURE) { \
  95. zend_throw_exception(mongo_ce_GridFSException, "couldn't find " name, 19 TSRMLS_CC); \
  96. return FAILURE; \
  97. }
  98. #define READ_OBJ_PROP(type, obj, name) \
  99. zend_read_property(mongo_ce_##type, obj, name, strlen(name), NOISY TSRMLS_CC);
  100. #define TO_INT(size, len) { \
  101. if (Z_TYPE_PP(size) == IS_DOUBLE) { \
  102. len = (int)Z_DVAL_PP(size); \
  103. } else { \
  104. len = Z_LVAL_PP(size); \
  105. } \
  106. }
  107. #define ASSERT_SIZE(size) \
  108. if (size > self->chunkSize) { \
  109. char * err; \
  110. spprintf(&err, 0, "chunk %d has wrong size (%d) when the max is %d", chunk_id, size, self->chunkSize); \
  111. zend_throw_exception(mongo_ce_GridFSException, err, 20 TSRMLS_CC); \
  112. zval_ptr_dtor(&chunk); \
  113. return FAILURE; \
  114. } \
  115. /* }}} */
  116. /* {{{ php_stream* gridfs_stream_init(zval* file_object TSRMLS_DC) */
  117. php_stream* gridfs_stream_init(zval *file_object TSRMLS_DC)
  118. {
  119. gridfs_stream_data *self;
  120. php_stream *stream;
  121. zval *file, **id, **size, **chunkSize, *gridfs;
  122. file = READ_OBJ_PROP(GridFSFile, file_object, "file");
  123. READ_ARRAY_PROP_PTR(file, "_id", id);
  124. READ_ARRAY_PROP_PTR(file, "length", size);
  125. READ_ARRAY_PROP_PTR(file, "chunkSize", chunkSize);
  126. gridfs = READ_OBJ_PROP(GridFSFile, file_object, "gridfs");
  127. /* allocate memory and init the stream resource */
  128. self = (gridfs_stream_data*) emalloc(sizeof(*self));
  129. memset(self, 0, sizeof(*self));
  130. TO_INT(size, self->size);
  131. TO_INT(chunkSize, self->chunkSize);
  132. self->fileObj = file_object;
  133. self->chunkObj = READ_OBJ_PROP(GridFS, gridfs, "chunks");
  134. self->buffer = (unsigned char*) emalloc(self->chunkSize +1);
  135. self->id = *id;
  136. self->chunkId = -1;
  137. self->totalChunks = ceil(self->size/self->chunkSize);
  138. zval_add_ref(&self->fileObj);
  139. zval_add_ref(&self->chunkObj);
  140. zval_add_ref(&self->id);
  141. /* create base query object */
  142. MAKE_STD_ZVAL(self->query);
  143. array_init(self->query);
  144. add_assoc_zval(self->query, "files_id", self->id);
  145. zval_add_ref(&self->id);
  146. stream = php_stream_alloc(&gridfs_stream_ops, self, 0, "rb");
  147. return stream;
  148. }
  149. /* }}} */
  150. /* {{{ array fstat($fp) */
  151. static int gridfs_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC)
  152. {
  153. gridfs_stream_data *self = (gridfs_stream_data *) stream->abstract;
  154. ssb->sb.st_size = self->size;
  155. return SUCCESS;
  156. }
  157. /* }}} */
  158. /* {{{ int gridfs_read_chunk(gridfs_stream_data *self, int chunk_id) */
  159. static int gridfs_read_chunk(gridfs_stream_data *self, int chunk_id TSRMLS_DC)
  160. {
  161. zval *chunk = 0, **data, *bin;
  162. if (chunk_id == -1) {
  163. /* we need to figure out which chunk to load */
  164. chunk_id = (int)(self->offset / self->chunkSize);
  165. }
  166. if (chunk_id == self->chunkId) {
  167. /* nothing to load :-) */
  168. return SUCCESS;
  169. }
  170. PRINTF_DEBUG(("loading chunk %d\n", chunk_id));
  171. add_assoc_long(self->query, "n", chunk_id);
  172. MAKE_STD_ZVAL(chunk);
  173. MONGO_METHOD1(MongoCollection, findOne, chunk, self->chunkObj, self->query);
  174. if (Z_TYPE_P(chunk) == IS_NULL) {
  175. zval_ptr_dtor(&chunk);
  176. return FAILURE;
  177. }
  178. READ_ARRAY_PROP(chunk, "data", data);
  179. if (Z_TYPE_PP(data) == IS_STRING) {
  180. ASSERT_SIZE(Z_STRLEN_PP(data))
  181. memcpy(self->buffer, Z_STRVAL_PP(data), Z_STRLEN_PP(data));
  182. self->buffer_size = Z_STRLEN_PP(data);
  183. } else if (Z_TYPE_PP(data) == IS_OBJECT && Z_OBJCE_PP(data) == mongo_ce_BinData) {
  184. bin = READ_OBJ_PROP(BinData, *data, "bin");
  185. ASSERT_SIZE(Z_STRLEN_P(bin))
  186. memcpy(self->buffer, Z_STRVAL_P(bin), Z_STRLEN_P(bin));
  187. self->buffer_size = Z_STRLEN_P(bin);
  188. } else {
  189. zend_throw_exception(mongo_ce_GridFSException, "chunk has wrong format", 21 TSRMLS_CC);
  190. zval_ptr_dtor(&chunk);
  191. return FAILURE;
  192. }
  193. self->chunkId = chunk_id;
  194. self->buffer_offset = self->offset % self->chunkSize;
  195. zval_ptr_dtor(&chunk);
  196. return SUCCESS;
  197. }
  198. /* }}} */
  199. /* {{{ fread($fp, $bytes) */
  200. static size_t gridfs_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
  201. {
  202. gridfs_stream_data *self = (gridfs_stream_data *) stream->abstract;
  203. int size, chunk_id;
  204. /* load the needed chunk from mongo */
  205. chunk_id = (int)((self->offset)/self->chunkSize);
  206. if (gridfs_read_chunk(self, chunk_id TSRMLS_CC) == FAILURE) {
  207. return -1;
  208. }
  209. size = MIN(count, self->buffer_size - self->buffer_offset % self->chunkSize);
  210. memcpy(buf, self->buffer + self->buffer_offset % self->chunkSize, size);
  211. if (size < count && chunk_id + 1 < self->totalChunks) {
  212. int tmp_bytes;
  213. /* load next chunk to return the exact requested bytes */
  214. if (gridfs_read_chunk(self, chunk_id + 1 TSRMLS_CC) == FAILURE) {
  215. return -1;
  216. }
  217. tmp_bytes = MIN(count-size, self->buffer_size);
  218. memcpy(buf+size, self->buffer, tmp_bytes);
  219. size += tmp_bytes;
  220. }
  221. self->buffer_offset += size;
  222. self->offset += size;
  223. PRINTF_DEBUG(("offset=%d (+%d)\n", self->offset, size));
  224. return size;
  225. }
  226. /* }}} */
  227. /* {{{ fseek($fp, $bytes, $whence) */
  228. static int gridfs_seek(php_stream *stream, off_t offset, int whence, off_t *newoffs TSRMLS_DC)
  229. {
  230. gridfs_stream_data *self = (gridfs_stream_data *) stream->abstract;
  231. int newoffset = 0;
  232. switch (whence) {
  233. case SEEK_SET:
  234. newoffset = offset;
  235. break;
  236. case SEEK_CUR:
  237. newoffset = self->offset + offset;
  238. break;
  239. case SEEK_END:
  240. newoffset = self->size + offset;
  241. break;
  242. default:
  243. return FAILURE;
  244. }
  245. if (newoffset > self->size) {
  246. return FAILURE;
  247. }
  248. *newoffs = newoffset;
  249. self->offset = newoffset;
  250. if (self->chunkId != -1) {
  251. /* change the offset also in the chunk */
  252. self->buffer_offset = newoffset % self->chunkSize;
  253. }
  254. return SUCCESS;
  255. }
  256. /* }}} */
  257. /* {{{ fclose($fp) */
  258. static int gridfs_close(php_stream *stream, int close_handle TSRMLS_DC)
  259. {
  260. gridfs_stream_data *self = (gridfs_stream_data *) stream->abstract;
  261. zval_ptr_dtor(&self->fileObj);
  262. zval_ptr_dtor(&self->chunkObj);
  263. zval_ptr_dtor(&self->query);
  264. zval_ptr_dtor(&self->id);
  265. efree(self->buffer);
  266. efree(self);
  267. return 0;
  268. }
  269. /* }}} */
  270. /* {{{ feof */
  271. static int gridfs_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC)
  272. {
  273. gridfs_stream_data * self = (gridfs_stream_data *) stream->abstract;
  274. int ret = -1;
  275. switch (option) {
  276. case PHP_STREAM_OPTION_CHECK_LIVENESS:
  277. ret = self->size == self->offset ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK;
  278. break;
  279. }
  280. return ret;
  281. }
  282. /* }}} */
  283. /*
  284. * Local variables:
  285. * tab-width: 4
  286. * c-basic-offset: 4
  287. * End:
  288. * vim600: fdm=marker
  289. * vim: noet sw=4 ts=4
  290. */