/hphp/runtime/ext_zend_compat/mongo/gridfs/gridfs_stream.cpp
C++ | 368 lines | 233 code | 78 blank | 57 comment | 34 complexity | d4896441a3f31a7134a39dce8c4f1752 MD5 | raw file
- /**
- * Copyright 2009-2013 10gen, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * Author: César D. Rodas <crodas@php.net>
- */
- #include <php.h>
- #ifdef WIN32
- # ifndef int64_t
- typedef __int64 int64_t;
- # endif
- #endif
- #include <php_globals.h>
- #include <ext/standard/file.h>
- #include <ext/standard/flock_compat.h>
- #ifdef HAVE_SYS_FILE_H
- # include <sys/file.h>
- #endif
- #include <zend_exceptions.h>
- #include "../php_mongo.h"
- #include "gridfs.h"
- #include "gridfs_stream.h"
- #include "../collection.h"
- #include "../cursor.h"
- #include "../db.h"
- extern zend_class_entry *mongo_ce_BinData, *mongo_ce_GridFS;
- extern zend_class_entry *mongo_ce_GridFSFile, *mongo_ce_GridFSException;
- ZEND_EXTERN_MODULE_GLOBALS(mongo)
- static size_t gridfs_read(php_stream *stream, char *buf, size_t count TSRMLS_DC);
- static int gridfs_close(php_stream *stream, int close_handle TSRMLS_DC);
- static int gridfs_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC);
- static int gridfs_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC);
- static int gridfs_seek(php_stream *stream, off_t offset, int whence, off_t *newoffs TSRMLS_DC);
- typedef struct _gridfs_stream_data {
- zval * fileObj; /* MongoGridFSFile Object */
- zval * chunkObj; /* Chunk collection object */
- zval * id; /* File ID */
- zval * query; /* Query array */
- /* file current position */
- size_t offset;
- /* file size */
- int size;
- /* chunk size */
- int chunkSize;
- int totalChunks;
- /* which chunk is loaded? */
- int chunkId;
- /* mongo current chunk is kept in memory */
- unsigned char * buffer;
- /* chunk size */
- int buffer_size;
- /* where we are in the chunk? */
- size_t buffer_offset;
- } gridfs_stream_data;
- php_stream_ops gridfs_stream_ops = {
- NULL, /* write */
- gridfs_read, /* read */
- gridfs_close, /* close */
- NULL, /* flush */
- "gridfs-wrapper",
- gridfs_seek, /* seek */
- NULL, /* cast */
- gridfs_stat, /* stat */
- gridfs_option, /* set_option */
- };
- /* some handy macros {{{ */
- #ifndef MIN
- # define MIN(a, b) a > b ? b : a
- #endif
- #if 0
- # define PRINTF_DEBUG(x) printf x;fflush(stdout);
- #else
- # define PRINTF_DEBUG(x)
- #endif
- /* returns 0 on failure */
- #define READ_ARRAY_PROP_PTR(dest, name, toVariable) \
- if (zend_hash_find(HASH_P(dest), name, strlen(name) + 1, (void**)&toVariable) == FAILURE) { \
- zend_throw_exception(mongo_ce_GridFSException, "couldn't find " name, 19 TSRMLS_CC); \
- return 0; \
- }
- /* returns FAILURE on failure */
- #define READ_ARRAY_PROP(dest, name, toVariable) \
- if (zend_hash_find(HASH_P(dest), name, strlen(name) + 1, (void**)&toVariable) == FAILURE) { \
- zend_throw_exception(mongo_ce_GridFSException, "couldn't find " name, 19 TSRMLS_CC); \
- return FAILURE; \
- }
- #define READ_OBJ_PROP(type, obj, name) \
- zend_read_property(mongo_ce_##type, obj, name, strlen(name), NOISY TSRMLS_CC);
- #define TO_INT(size, len) { \
- if (Z_TYPE_PP(size) == IS_DOUBLE) { \
- len = (int)Z_DVAL_PP(size); \
- } else { \
- len = Z_LVAL_PP(size); \
- } \
- }
- #define ASSERT_SIZE(size) \
- if (size > self->chunkSize) { \
- char * err; \
- spprintf(&err, 0, "chunk %d has wrong size (%d) when the max is %d", chunk_id, size, self->chunkSize); \
- zend_throw_exception(mongo_ce_GridFSException, err, 20 TSRMLS_CC); \
- zval_ptr_dtor(&chunk); \
- return FAILURE; \
- } \
- /* }}} */
- /* {{{ php_stream* gridfs_stream_init(zval* file_object TSRMLS_DC) */
- php_stream* gridfs_stream_init(zval *file_object TSRMLS_DC)
- {
- gridfs_stream_data *self;
- php_stream *stream;
- zval *file, **id, **size, **chunkSize, *gridfs;
- file = READ_OBJ_PROP(GridFSFile, file_object, "file");
- READ_ARRAY_PROP_PTR(file, "_id", id);
- READ_ARRAY_PROP_PTR(file, "length", size);
- READ_ARRAY_PROP_PTR(file, "chunkSize", chunkSize);
- gridfs = READ_OBJ_PROP(GridFSFile, file_object, "gridfs");
- /* allocate memory and init the stream resource */
- self = (gridfs_stream_data*) emalloc(sizeof(*self));
- memset(self, 0, sizeof(*self));
- TO_INT(size, self->size);
- TO_INT(chunkSize, self->chunkSize);
- self->fileObj = file_object;
- self->chunkObj = READ_OBJ_PROP(GridFS, gridfs, "chunks");
- self->buffer = (unsigned char*) emalloc(self->chunkSize +1);
- self->id = *id;
- self->chunkId = -1;
- self->totalChunks = ceil(self->size/self->chunkSize);
- zval_add_ref(&self->fileObj);
- zval_add_ref(&self->chunkObj);
- zval_add_ref(&self->id);
- /* create base query object */
- MAKE_STD_ZVAL(self->query);
- array_init(self->query);
- add_assoc_zval(self->query, "files_id", self->id);
- zval_add_ref(&self->id);
- stream = php_stream_alloc(&gridfs_stream_ops, self, 0, "rb");
- return stream;
- }
- /* }}} */
- /* {{{ array fstat($fp) */
- static int gridfs_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC)
- {
- gridfs_stream_data *self = (gridfs_stream_data *) stream->abstract;
- ssb->sb.st_size = self->size;
- return SUCCESS;
- }
- /* }}} */
- /* {{{ int gridfs_read_chunk(gridfs_stream_data *self, int chunk_id) */
- static int gridfs_read_chunk(gridfs_stream_data *self, int chunk_id TSRMLS_DC)
- {
- zval *chunk = 0, **data, *bin;
- if (chunk_id == -1) {
- /* we need to figure out which chunk to load */
- chunk_id = (int)(self->offset / self->chunkSize);
- }
- if (chunk_id == self->chunkId) {
- /* nothing to load :-) */
- return SUCCESS;
- }
- PRINTF_DEBUG(("loading chunk %d\n", chunk_id));
- add_assoc_long(self->query, "n", chunk_id);
- MAKE_STD_ZVAL(chunk);
- MONGO_METHOD1(MongoCollection, findOne, chunk, self->chunkObj, self->query);
- if (Z_TYPE_P(chunk) == IS_NULL) {
- zval_ptr_dtor(&chunk);
- return FAILURE;
- }
- READ_ARRAY_PROP(chunk, "data", data);
- if (Z_TYPE_PP(data) == IS_STRING) {
- ASSERT_SIZE(Z_STRLEN_PP(data))
- memcpy(self->buffer, Z_STRVAL_PP(data), Z_STRLEN_PP(data));
- self->buffer_size = Z_STRLEN_PP(data);
- } else if (Z_TYPE_PP(data) == IS_OBJECT && Z_OBJCE_PP(data) == mongo_ce_BinData) {
- bin = READ_OBJ_PROP(BinData, *data, "bin");
- ASSERT_SIZE(Z_STRLEN_P(bin))
- memcpy(self->buffer, Z_STRVAL_P(bin), Z_STRLEN_P(bin));
- self->buffer_size = Z_STRLEN_P(bin);
- } else {
- zend_throw_exception(mongo_ce_GridFSException, "chunk has wrong format", 21 TSRMLS_CC);
- zval_ptr_dtor(&chunk);
- return FAILURE;
- }
- self->chunkId = chunk_id;
- self->buffer_offset = self->offset % self->chunkSize;
- zval_ptr_dtor(&chunk);
- return SUCCESS;
- }
- /* }}} */
- /* {{{ fread($fp, $bytes) */
- static size_t gridfs_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
- {
- gridfs_stream_data *self = (gridfs_stream_data *) stream->abstract;
- int size, chunk_id;
- /* load the needed chunk from mongo */
- chunk_id = (int)((self->offset)/self->chunkSize);
- if (gridfs_read_chunk(self, chunk_id TSRMLS_CC) == FAILURE) {
- return -1;
- }
- size = MIN(count, self->buffer_size - self->buffer_offset % self->chunkSize);
- memcpy(buf, self->buffer + self->buffer_offset % self->chunkSize, size);
- if (size < count && chunk_id + 1 < self->totalChunks) {
- int tmp_bytes;
- /* load next chunk to return the exact requested bytes */
- if (gridfs_read_chunk(self, chunk_id + 1 TSRMLS_CC) == FAILURE) {
- return -1;
- }
- tmp_bytes = MIN(count-size, self->buffer_size);
- memcpy(buf+size, self->buffer, tmp_bytes);
- size += tmp_bytes;
- }
- self->buffer_offset += size;
- self->offset += size;
- PRINTF_DEBUG(("offset=%d (+%d)\n", self->offset, size));
- return size;
- }
- /* }}} */
- /* {{{ fseek($fp, $bytes, $whence) */
- static int gridfs_seek(php_stream *stream, off_t offset, int whence, off_t *newoffs TSRMLS_DC)
- {
- gridfs_stream_data *self = (gridfs_stream_data *) stream->abstract;
- int newoffset = 0;
- switch (whence) {
- case SEEK_SET:
- newoffset = offset;
- break;
- case SEEK_CUR:
- newoffset = self->offset + offset;
- break;
- case SEEK_END:
- newoffset = self->size + offset;
- break;
- default:
- return FAILURE;
- }
- if (newoffset > self->size) {
- return FAILURE;
- }
- *newoffs = newoffset;
- self->offset = newoffset;
- if (self->chunkId != -1) {
- /* change the offset also in the chunk */
- self->buffer_offset = newoffset % self->chunkSize;
- }
- return SUCCESS;
- }
- /* }}} */
- /* {{{ fclose($fp) */
- static int gridfs_close(php_stream *stream, int close_handle TSRMLS_DC)
- {
- gridfs_stream_data *self = (gridfs_stream_data *) stream->abstract;
- zval_ptr_dtor(&self->fileObj);
- zval_ptr_dtor(&self->chunkObj);
- zval_ptr_dtor(&self->query);
- zval_ptr_dtor(&self->id);
- efree(self->buffer);
- efree(self);
- return 0;
- }
- /* }}} */
- /* {{{ feof */
- static int gridfs_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC)
- {
- gridfs_stream_data * self = (gridfs_stream_data *) stream->abstract;
- int ret = -1;
- switch (option) {
- case PHP_STREAM_OPTION_CHECK_LIVENESS:
- ret = self->size == self->offset ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK;
- break;
- }
- return ret;
- }
- /* }}} */
- /*
- * Local variables:
- * tab-width: 4
- * c-basic-offset: 4
- * End:
- * vim600: fdm=marker
- * vim: noet sw=4 ts=4
- */