1 /**
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  *   http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 
20 #include <ruby.h>
21 #include <constants.h>
22 #include <bytes.h>
23 #include <macros.h>
24 
25 ID buf_ivar_id;
26 ID index_ivar_id;
27 
28 ID slice_method_id;
29 
30 int GARBAGE_BUFFER_SIZE;
31 
32 #define GET_BUF(self) rb_ivar_get(self, buf_ivar_id)
33 
34 VALUE rb_thrift_memory_buffer_write(VALUE self, VALUE str);
35 VALUE rb_thrift_memory_buffer_read(VALUE self, VALUE length_value);
36 VALUE rb_thrift_memory_buffer_read_byte(VALUE self);
37 VALUE rb_thrift_memory_buffer_read_into_buffer(VALUE self, VALUE buffer_value, VALUE size_value);
38 
rb_thrift_memory_buffer_write(VALUE self,VALUE str)39 VALUE rb_thrift_memory_buffer_write(VALUE self, VALUE str) {
40   VALUE buf = GET_BUF(self);
41   str = force_binary_encoding(str);
42   rb_str_buf_cat(buf, StringValuePtr(str), RSTRING_LEN(str));
43   return Qnil;
44 }
45 
rb_thrift_memory_buffer_read(VALUE self,VALUE length_value)46 VALUE rb_thrift_memory_buffer_read(VALUE self, VALUE length_value) {
47   int length = FIX2INT(length_value);
48 
49   VALUE index_value = rb_ivar_get(self, index_ivar_id);
50   int index = FIX2INT(index_value);
51 
52   VALUE buf = GET_BUF(self);
53   VALUE data = rb_funcall(buf, slice_method_id, 2, index_value, length_value);
54 
55   index += length;
56   if (index > RSTRING_LEN(buf)) {
57     index = RSTRING_LEN(buf);
58   }
59   if (index >= GARBAGE_BUFFER_SIZE) {
60     rb_ivar_set(self, buf_ivar_id, rb_funcall(buf, slice_method_id, 2, INT2FIX(index), INT2FIX(RSTRING_LEN(buf) - 1)));
61     index = 0;
62   }
63   rb_ivar_set(self, index_ivar_id, INT2FIX(index));
64 
65   if (RSTRING_LEN(data) < length) {
66     rb_raise(rb_eEOFError, "Not enough bytes remain in memory buffer");
67   }
68 
69   return data;
70 }
71 
rb_thrift_memory_buffer_read_byte(VALUE self)72 VALUE rb_thrift_memory_buffer_read_byte(VALUE self) {
73   VALUE index_value = rb_ivar_get(self, index_ivar_id);
74   int index = FIX2INT(index_value);
75 
76   VALUE buf = GET_BUF(self);
77   if (index >= RSTRING_LEN(buf)) {
78     rb_raise(rb_eEOFError, "Not enough bytes remain in memory buffer");
79   }
80   char byte = RSTRING_PTR(buf)[index++];
81 
82   if (index >= GARBAGE_BUFFER_SIZE) {
83     rb_ivar_set(self, buf_ivar_id, rb_funcall(buf, slice_method_id, 2, INT2FIX(index), INT2FIX(RSTRING_LEN(buf) - 1)));
84     index = 0;
85   }
86   rb_ivar_set(self, index_ivar_id, INT2FIX(index));
87 
88   int result = (int) byte;
89   return INT2FIX(result);
90 }
91 
rb_thrift_memory_buffer_read_into_buffer(VALUE self,VALUE buffer_value,VALUE size_value)92 VALUE rb_thrift_memory_buffer_read_into_buffer(VALUE self, VALUE buffer_value, VALUE size_value) {
93   int i = 0;
94   int size = FIX2INT(size_value);
95   int index;
96   VALUE buf = GET_BUF(self);
97 
98   index = FIX2INT(rb_ivar_get(self, index_ivar_id));
99   while (i < size) {
100     if (index >= RSTRING_LEN(buf)) {
101       rb_raise(rb_eEOFError, "Not enough bytes remain in memory buffer");
102     }
103     char byte = RSTRING_PTR(buf)[index++];
104 
105     if (i >= RSTRING_LEN(buffer_value)) {
106       rb_raise(rb_eIndexError, "index %d out of string", i);
107     }
108     ((char*)RSTRING_PTR(buffer_value))[i] = byte;
109     i++;
110   }
111 
112   if (index >= GARBAGE_BUFFER_SIZE) {
113     rb_ivar_set(self, buf_ivar_id, rb_funcall(buf, slice_method_id, 2, INT2FIX(index), INT2FIX(RSTRING_LEN(buf) - 1)));
114     index = 0;
115   }
116   rb_ivar_set(self, index_ivar_id, INT2FIX(index));
117 
118   return INT2FIX(i);
119 }
120 
Init_memory_buffer()121 void Init_memory_buffer() {
122   VALUE thrift_memory_buffer_class = rb_const_get(thrift_module, rb_intern("MemoryBufferTransport"));
123   rb_define_method(thrift_memory_buffer_class, "write", rb_thrift_memory_buffer_write, 1);
124   rb_define_method(thrift_memory_buffer_class, "read", rb_thrift_memory_buffer_read, 1);
125   rb_define_method(thrift_memory_buffer_class, "read_byte", rb_thrift_memory_buffer_read_byte, 0);
126   rb_define_method(thrift_memory_buffer_class, "read_into_buffer", rb_thrift_memory_buffer_read_into_buffer, 2);
127 
128   buf_ivar_id = rb_intern("@buf");
129   index_ivar_id = rb_intern("@index");
130 
131   slice_method_id = rb_intern("slice");
132 
133   GARBAGE_BUFFER_SIZE = FIX2INT(rb_const_get(thrift_memory_buffer_class, rb_intern("GARBAGE_BUFFER_SIZE")));
134 }
135