-
Notifications
You must be signed in to change notification settings - Fork 90
/
reader.c
124 lines (103 loc) · 3.99 KB
/
reader.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#include <assert.h>
#include "hiredis_ext.h"
/* Force encoding on new strings? */
static VALUE enc_klass;
static ID enc_default_external = 0;
static ID str_force_encoding = 0;
/* Add VALUE to parent when the redisReadTask has a parent.
* Note that the parent should always be of type T_ARRAY. */
static void *tryParentize(const redisReadTask *task, VALUE v) {
if (task && task->parent != NULL) {
volatile VALUE parent = (VALUE)task->parent->obj;
assert(TYPE(parent) == T_ARRAY);
rb_ary_store(parent,task->idx,v);
}
return (void*)v;
}
static void *createStringObject(const redisReadTask *task, char *str, size_t len) {
volatile VALUE v, enc;
v = rb_str_new(str,len);
/* Force default external encoding if possible. */
if (enc_default_external) {
enc = rb_funcall(enc_klass,enc_default_external,0);
v = rb_funcall(v,str_force_encoding,1,enc);
}
if (task->type == REDIS_REPLY_ERROR) {
v = rb_funcall(rb_eRuntimeError,rb_intern("new"),1,v);
}
return tryParentize(task,v);
}
static void *createArrayObject(const redisReadTask *task, size_t elements) {
volatile VALUE v = rb_ary_new2(elements);
return tryParentize(task,v);
}
static void *createIntegerObject(const redisReadTask *task, long long value) {
volatile VALUE v = LL2NUM(value);
return tryParentize(task,v);
}
static void *createNilObject(const redisReadTask *task) {
return tryParentize(task,Qnil);
}
static void freeObject(void *ptr) {
/* Garbage collection will clean things up. */
}
/* Declare our set of reply object functions only once. */
redisReplyObjectFunctions redisExtReplyObjectFunctions = {
createStringObject,
createArrayObject,
createIntegerObject,
NULL,
createNilObject,
NULL,
freeObject
};
static void reader_mark(redisReader *reader) {
// volatile until rb_gc_mark
volatile VALUE root = (VALUE)reader->reply;
// FIXME - PCO - checking root for 0 is checkign to see if the value is
// Qfalse. I suspect that is not what is intended here. Checking the
// redisReader code might clarify. It would be unfortunate if the reply, a
// void* was using NULL to indicate not set but that may be the nature of
// the redisReader library. It is worth checking anyway.
if (root != 0 && TYPE(root) == T_ARRAY) rb_gc_mark(root);
}
static VALUE reader_allocate(VALUE klass) {
redisReader *reader = redisReaderCreate();
reader->fn = &redisExtReplyObjectFunctions;
return Data_Wrap_Struct(klass, reader_mark, redisReaderFree, reader);
}
static VALUE reader_feed(VALUE klass, VALUE str) {
redisReader *reader;
if (TYPE(str) != T_STRING)
rb_raise(rb_eTypeError, "not a string");
Data_Get_Struct(klass, redisReader, reader);
redisReaderFeed(reader,RSTRING_PTR(str),(size_t)RSTRING_LEN(str));
return INT2NUM(0);
}
static VALUE reader_gets(VALUE klass) {
redisReader *reader;
volatile VALUE reply;
Data_Get_Struct(klass, redisReader, reader);
if (redisReaderGetReply(reader,(void**)&reply) != REDIS_OK)
rb_raise(rb_eRuntimeError,"%s",reader->errstr);
return reply;
}
VALUE klass_reader;
void InitReader(VALUE mod) {
klass_reader = rb_define_class_under(mod, "Reader", rb_cObject);
rb_global_variable(&klass_reader);
rb_define_alloc_func(klass_reader, reader_allocate);
rb_define_method(klass_reader, "feed", reader_feed, 1);
rb_define_method(klass_reader, "gets", reader_gets, 0);
/* If the Encoding class is present, #default_external should be used to
* determine the encoding for new strings. The "enc_default_external"
* ID is non-zero when encoding should be set on new strings. */
if (rb_const_defined(rb_cObject, rb_intern("Encoding"))) {
enc_klass = rb_const_get(rb_cObject, rb_intern("Encoding"));
enc_default_external = rb_intern("default_external");
str_force_encoding = rb_intern("force_encoding");
rb_global_variable(&enc_klass);
} else {
enc_default_external = 0;
}
}