Skip to content

Commit 7843292

Browse files
committed
wip PHPC-2474: BinaryVector support
1 parent f15d862 commit 7843292

11 files changed

+443
-12
lines changed

php_phongo.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ PHP_MINIT_FUNCTION(mongodb) /* {{{ */
235235
php_phongo_document_init_ce(INIT_FUNC_ARGS_PASSTHRU);
236236

237237
php_phongo_binary_init_ce(INIT_FUNC_ARGS_PASSTHRU);
238+
php_phongo_binaryvector_init_ce(INIT_FUNC_ARGS_PASSTHRU);
238239
php_phongo_dbpointer_init_ce(INIT_FUNC_ARGS_PASSTHRU);
239240
php_phongo_decimal128_init_ce(INIT_FUNC_ARGS_PASSTHRU);
240241
php_phongo_int64_init_ce(INIT_FUNC_ARGS_PASSTHRU);

src/BSON/Binary.c

Lines changed: 257 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@
2020

2121
#include "php_phongo.h"
2222
#include "phongo_error.h"
23+
#include "Binary.h"
2324
#include "Binary_arginfo.h"
24-
25-
#define PHONGO_BINARY_UUID_SIZE 16
25+
#include "BinaryVector_arginfo.h"
2626

2727
zend_class_entry* php_phongo_binary_ce;
2828

29+
static phongo_bson_vector_type_t phongo_binary_get_vector_type_from_data(const uint8_t* data, uint32_t data_len);
30+
2931
/* Initialize the object and return whether it was successful. An exception will
3032
* be thrown on error. */
3133
static bool php_phongo_binary_init(php_phongo_binary_t* intern, const char* data, size_t data_len, zend_long type)
@@ -40,6 +42,11 @@ static bool php_phongo_binary_init(php_phongo_binary_t* intern, const char* data
4042
return false;
4143
}
4244

45+
if ((type == BSON_SUBTYPE_VECTOR) && phongo_binary_get_vector_type_from_data((const uint8_t*) data, data_len) == PHONGO_BSON_VECTOR_TYPE_INVALID) {
46+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Binary vector data is invalid");
47+
return false;
48+
}
49+
4350
intern->data = estrndup(data, data_len);
4451
intern->data_len = data_len;
4552
intern->type = (uint8_t) type;
@@ -299,6 +306,7 @@ bool phongo_binary_new(zval* object, const char* data, size_t data_len, bson_sub
299306
{
300307
php_phongo_binary_t* intern;
301308

309+
// TODO: Support BinaryVector
302310
object_init_ex(object, php_phongo_binary_ce);
303311

304312
intern = Z_BINARY_OBJ_P(object);
@@ -308,3 +316,250 @@ bool phongo_binary_new(zval* object, const char* data, size_t data_len, bson_sub
308316

309317
return true;
310318
}
319+
320+
/* MongoDB\BSON\BinaryVector implementation */
321+
322+
zend_class_entry* php_phongo_binaryvector_ce;
323+
324+
static PHP_METHOD(MongoDB_BSON_BinaryVector, __construct)
325+
{
326+
php_phongo_binary_t* intern;
327+
zval* vector;
328+
zend_long vector_type;
329+
330+
intern = Z_BINARY_OBJ_P(getThis());
331+
332+
PHONGO_PARSE_PARAMETERS_START(2, 2)
333+
Z_PARAM_ARRAY(vector)
334+
Z_PARAM_LONG(vector_type)
335+
PHONGO_PARSE_PARAMETERS_END();
336+
337+
if (!zend_array_is_list(Z_ARRVAL_P(vector))) {
338+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Expected vector to be a list");
339+
return;
340+
}
341+
342+
const size_t vector_len = zend_array_count(Z_ARRVAL_P(vector));
343+
344+
/*
345+
bson_t doc = BSON_INITIALIZER;
346+
347+
php_phongo_zval_to_bson(vector, PHONGO_BSON_NONE, &doc, NULL);
348+
349+
if (EG(exception)) {
350+
return;
351+
}
352+
353+
bson_iter_t iter;
354+
bson_error_t error;
355+
356+
bson_iter_init(&iter, &doc);
357+
358+
if (!BSON_APPEND_VECTOR_INT8_FROM_ARRAY(&bson, "vector", &iter, &err)) {
359+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "%s", error.message);
360+
return;
361+
}
362+
*/
363+
364+
bson_t doc = BSON_INITIALIZER;
365+
366+
if (vector_type == PHONGO_BSON_VECTOR_TYPE_INT8) {
367+
bson_vector_int8_view_t view;
368+
369+
if (!BSON_APPEND_VECTOR_INT8_UNINIT(&doc, "vector", vector_len, &view)) {
370+
phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "BSON_APPEND_VECTOR_INT8_UNINIT failed for vector of size %zu", vector_len);
371+
RETURN_THROWS();
372+
}
373+
374+
zval* val;
375+
size_t i = 0;
376+
377+
ZEND_HASH_FOREACH_VAL_IND(Z_ARRVAL_P(vector), val)
378+
{
379+
if (Z_TYPE_P(val) != IS_LONG) {
380+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Expected vector[%zu] to be an integer, %s given", i, zend_zval_type_name(val));
381+
RETURN_THROWS();
382+
}
383+
384+
if (Z_LVAL_P(val) < INT8_MIN || Z_LVAL_P(val) > INT8_MAX) {
385+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Expected vector[%zu] to be a signed 8-bit integer, %" PHONGO_LONG_FORMAT " given", i, Z_LVAL_P(val));
386+
RETURN_THROWS();
387+
}
388+
389+
int8_t v = (int8_t) Z_LVAL_P(val);
390+
391+
if (!bson_vector_int8_view_write(view, &v, 1, i)) {
392+
phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "bson_vector_int8_view_write failed to write vector[%zu]", i);
393+
RETURN_THROWS();
394+
}
395+
396+
i += 1;
397+
}
398+
ZEND_HASH_FOREACH_END();
399+
} else {
400+
// TODO: Support PHONGO_BSON_VECTOR_TYPE_FLOAT32 and PHONGO_BSON_VECTOR_TYPE_PACKED_BIT
401+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Unsupported vector type: %" PHONGO_LONG_FORMAT, vector_type);
402+
RETURN_THROWS();
403+
}
404+
405+
bson_iter_t iter;
406+
407+
if (bson_iter_init_find(&iter, &doc, "vector") && BSON_ITER_HOLDS_BINARY(&iter)) {
408+
uint32_t data_len;
409+
const uint8_t* data;
410+
411+
bson_iter_binary(&iter, NULL, &data_len, &data);
412+
php_phongo_binary_init(intern, (const char*) data, data_len, BSON_SUBTYPE_VECTOR);
413+
}
414+
}
415+
416+
static phongo_bson_vector_type_t phongo_binary_get_vector_type_from_data(const uint8_t* data, uint32_t data_len)
417+
{
418+
if (bson_vector_int8_const_view_init(NULL, data, data_len)) {
419+
return PHONGO_BSON_VECTOR_TYPE_INT8;
420+
}
421+
422+
if (bson_vector_float32_const_view_init(NULL, data, data_len)) {
423+
return PHONGO_BSON_VECTOR_TYPE_FLOAT32;
424+
}
425+
426+
if (bson_vector_packed_bit_const_view_init(NULL, data, data_len)) {
427+
return PHONGO_BSON_VECTOR_TYPE_PACKED_BIT;
428+
}
429+
430+
return PHONGO_BSON_VECTOR_TYPE_INVALID;
431+
}
432+
433+
static phongo_bson_vector_type_t phongo_binary_get_vector_type(const php_phongo_binary_t* intern)
434+
{
435+
return phongo_binary_get_vector_type_from_data((const uint8_t*) intern->data, intern->data_len);
436+
}
437+
438+
static PHP_METHOD(MongoDB_BSON_BinaryVector, getVectorType)
439+
{
440+
PHONGO_PARSE_PARAMETERS_NONE();
441+
442+
php_phongo_binary_t* intern = Z_BINARY_OBJ_P(getThis());
443+
phongo_bson_vector_type_t type = phongo_binary_get_vector_type(intern);
444+
445+
// The vector should always be valid by this point, but check for an error
446+
if (type == PHONGO_BSON_VECTOR_TYPE_INVALID) {
447+
phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Binary vector data is invalid");
448+
RETURN_THROWS();
449+
}
450+
451+
RETURN_LONG(type);
452+
}
453+
454+
static PHP_METHOD(MongoDB_BSON_BinaryVector, toArray)
455+
{
456+
PHONGO_PARSE_PARAMETERS_NONE();
457+
458+
php_phongo_binary_t* intern = Z_BINARY_OBJ_P(getThis());
459+
phongo_bson_vector_type_t type = phongo_binary_get_vector_type(intern);
460+
461+
// The vector should always be valid by this point, but check for an error
462+
if (type == PHONGO_BSON_VECTOR_TYPE_INVALID) {
463+
phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Binary vector data is invalid");
464+
RETURN_THROWS();
465+
}
466+
467+
bson_t doc = BSON_INITIALIZER;
468+
469+
if (type == PHONGO_BSON_VECTOR_TYPE_INT8) {
470+
bson_vector_int8_const_view_t view;
471+
472+
if (!bson_vector_int8_const_view_init(&view, (const uint8_t*) intern->data, intern->data_len) ||
473+
!BSON_APPEND_ARRAY_FROM_VECTOR_INT8(&doc, "vector", view)) {
474+
phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Failed to convert binary vector data to an array");
475+
bson_destroy(&doc);
476+
RETURN_THROWS();
477+
}
478+
} else if (type == PHONGO_BSON_VECTOR_TYPE_FLOAT32) {
479+
bson_vector_float32_const_view_t view;
480+
481+
if (!bson_vector_float32_const_view_init(&view, (const uint8_t*) intern->data, intern->data_len) ||
482+
!BSON_APPEND_ARRAY_FROM_VECTOR_FLOAT32(&doc, "vector", view)) {
483+
phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Failed to convert binary vector data to an array");
484+
bson_destroy(&doc);
485+
RETURN_THROWS();
486+
}
487+
} else if (type == PHONGO_BSON_VECTOR_TYPE_PACKED_BIT) {
488+
bson_vector_packed_bit_const_view_t view;
489+
490+
if (!bson_vector_packed_bit_const_view_init(&view, (const uint8_t*) intern->data, intern->data_len) ||
491+
!BSON_APPEND_ARRAY_FROM_VECTOR_PACKED_BIT(&doc, "vector", view)) {
492+
phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Failed to convert binary vector data to an array");
493+
bson_destroy(&doc);
494+
RETURN_THROWS();
495+
}
496+
}
497+
498+
php_phongo_bson_state state;
499+
PHONGO_BSON_INIT_STATE(state);
500+
state.is_visiting_array = true;
501+
502+
if (!php_phongo_bson_to_zval_ex(&doc, &state)) {
503+
// Exception already thrown
504+
bson_destroy(&doc);
505+
zval_ptr_dtor(&state.zchild);
506+
php_phongo_bson_typemap_dtor(&state.map);
507+
RETURN_THROWS();
508+
}
509+
510+
bson_destroy(&doc);
511+
php_phongo_bson_typemap_dtor(&state.map);
512+
513+
RETURN_ZVAL(&state.zchild, 0, 1);
514+
}
515+
516+
static PHP_METHOD(MongoDB_BSON_BinaryVector, __set_state)
517+
{
518+
php_phongo_binary_t* intern;
519+
HashTable* props;
520+
zval* array;
521+
522+
PHONGO_PARSE_PARAMETERS_START(1, 1)
523+
Z_PARAM_ARRAY(array)
524+
PHONGO_PARSE_PARAMETERS_END();
525+
526+
/* This implementation is similar to Binary::__set_state(), except that we
527+
* initialize a BinaryVector object and also validate its data. */
528+
object_init_ex(return_value, php_phongo_binaryvector_ce);
529+
530+
intern = Z_BINARY_OBJ_P(return_value);
531+
props = Z_ARRVAL_P(array);
532+
533+
php_phongo_binary_init_from_hash(intern, props);
534+
535+
if (intern->type != BSON_SUBTYPE_VECTOR || phongo_binary_get_vector_type(intern) == PHONGO_BSON_VECTOR_TYPE_INVALID) {
536+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Binary vector data is invalid");
537+
RETURN_THROWS();
538+
}
539+
}
540+
541+
/* MongoDB\BSON\BinaryVector object handlers */
542+
static zend_object_handlers php_phongo_handler_binaryvector;
543+
544+
static zend_object* php_phongo_binaryvector_create_object(zend_class_entry* class_type)
545+
{
546+
php_phongo_binary_t* intern = zend_object_alloc(sizeof(php_phongo_binary_t), class_type);
547+
548+
zend_object_std_init(&intern->std, class_type);
549+
object_properties_init(&intern->std, class_type);
550+
551+
intern->std.handlers = &php_phongo_handler_binaryvector;
552+
553+
return &intern->std;
554+
}
555+
556+
void php_phongo_binaryvector_init_ce(INIT_FUNC_ARGS)
557+
{
558+
php_phongo_binaryvector_ce = register_class_MongoDB_BSON_BinaryVector(php_phongo_binary_ce);
559+
php_phongo_binaryvector_ce->create_object = php_phongo_binaryvector_create_object;
560+
561+
memcpy(&php_phongo_handler_binaryvector, &php_phongo_handler_binary, sizeof(zend_object_handlers));
562+
// TODO: Override debug and prop handlers to include vector
563+
//php_phongo_handler_binaryvector.get_debug_info = php_phongo_binaryvector_get_debug_info;
564+
//php_phongo_handler_binaryvector.get_properties = php_phongo_binaryvector_get_properties;
565+
}

src/BSON/Binary.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@
1919

2020
#define PHONGO_BINARY_UUID_SIZE 16
2121

22+
typedef enum {
23+
PHONGO_BSON_VECTOR_TYPE_INVALID = 0,
24+
PHONGO_BSON_VECTOR_TYPE_INT8 = 0x03,
25+
PHONGO_BSON_VECTOR_TYPE_FLOAT32 = 0x27,
26+
PHONGO_BSON_VECTOR_TYPE_PACKED_BIT = 0x10,
27+
} phongo_bson_vector_type_t;
28+
2229
bool phongo_binary_new(zval* object, const char* data, size_t data_len, bson_subtype_t type);
2330

2431
#endif /* PHONGO_BSON_BINARY_H */

src/BSON/Binary.stub.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
namespace MongoDB\BSON;
99

10-
final class Binary implements BinaryInterface, \JsonSerializable, Type, \Stringable
10+
class Binary implements BinaryInterface, \JsonSerializable, Type, \Stringable
1111
{
1212
/**
1313
* @var int
@@ -63,19 +63,25 @@ final class Binary implements BinaryInterface, \JsonSerializable, Type, \Stringa
6363
*/
6464
public const TYPE_SENSITIVE = UNKNOWN;
6565

66+
/**
67+
* @var int
68+
* @cvalue BSON_SUBTYPE_VECTOR
69+
*/
70+
public const TYPE_VECTOR = UNKNOWN;
71+
6672
/**
6773
* @var int
6874
* @cvalue BSON_SUBTYPE_USER
6975
*/
7076
public const TYPE_USER_DEFINED = UNKNOWN;
7177

72-
final public function __construct(string $data, int $type = Binary::TYPE_GENERIC) {}
78+
public function __construct(string $data, int $type = Binary::TYPE_GENERIC) {}
7379

7480
final public function getData(): string {}
7581

7682
final public function getType(): int {}
7783

78-
final public static function __set_state(array $properties): Binary {}
84+
public static function __set_state(array $properties): Binary {}
7985

8086
final public function __toString(): string {}
8187

src/BSON/BinaryVector.stub.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
/**
4+
* @generate-class-entries static
5+
* @generate-function-entries static
6+
*/
7+
8+
namespace MongoDB\BSON;
9+
10+
class BinaryVector extends Binary
11+
{
12+
/**
13+
* @var int
14+
* @cvalue PHONGO_BSON_VECTOR_TYPE_INT8
15+
*/
16+
public const VECTOR_TYPE_INT8 = UNKNOWN;
17+
18+
/**
19+
* @var int
20+
* @cvalue PHONGO_BSON_VECTOR_TYPE_FLOAT32
21+
*/
22+
public const VECTOR_TYPE_FLOAT32 = UNKNOWN;
23+
24+
/**
25+
* @var int
26+
* @cvalue PHONGO_BSON_VECTOR_TYPE_PACKED_BIT
27+
*/
28+
public const VECTOR_TYPE_PACKED_BIT = UNKNOWN;
29+
30+
final public function __construct(array $vector, int $type) {}
31+
32+
final public function getVectorType(): int {}
33+
34+
final public function toArray(): array {}
35+
36+
final public static function __set_state(array $properties): BinaryVector {}
37+
}

0 commit comments

Comments
 (0)