Ruby  3.1.4p223 (2023-03-30 revision HEAD)
io_buffer.c
1 /**********************************************************************
2 
3  io_buffer.c
4 
5  Copyright (C) 2021 Samuel Grant Dawson Williams
6 
7 **********************************************************************/
8 
9 #include "ruby/io.h"
10 #include "ruby/io/buffer.h"
11 #include "ruby/fiber/scheduler.h"
12 
13 #include "internal.h"
14 #include "internal/string.h"
15 #include "internal/bits.h"
16 #include "internal/error.h"
17 
18 VALUE rb_cIOBuffer;
19 VALUE rb_eIOBufferLockedError;
20 VALUE rb_eIOBufferAllocationError;
21 VALUE rb_eIOBufferAccessError;
22 VALUE rb_eIOBufferInvalidatedError;
23 
24 size_t RUBY_IO_BUFFER_PAGE_SIZE;
25 size_t RUBY_IO_BUFFER_DEFAULT_SIZE;
26 
27 #ifdef _WIN32
28 #else
29 #include <unistd.h>
30 #include <sys/mman.h>
31 #endif
32 
33 struct rb_io_buffer {
34  void *base;
35  size_t size;
36  enum rb_io_buffer_flags flags;
37 
38 #if defined(_WIN32)
39  HANDLE mapping;
40 #endif
41 
42  VALUE source;
43 };
44 
45 static inline void *
46 io_buffer_map_memory(size_t size)
47 {
48 #if defined(_WIN32)
49  void * base = VirtualAlloc(0, size, MEM_COMMIT, PAGE_READWRITE);
50 
51  if (!base) {
52  rb_sys_fail("io_buffer_map_memory:VirtualAlloc");
53  }
54 #else
55  void * base = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
56 
57  if (base == MAP_FAILED) {
58  rb_sys_fail("io_buffer_map_memory:mmap");
59  }
60 #endif
61 
62  return base;
63 }
64 
65 static void
66 io_buffer_map_file(struct rb_io_buffer *data, int descriptor, size_t size, off_t offset, enum rb_io_buffer_flags flags)
67 {
68 #if defined(_WIN32)
69  HANDLE file = (HANDLE)_get_osfhandle(descriptor);
70  if (!file) rb_sys_fail("io_buffer_map_descriptor:_get_osfhandle");
71 
72  DWORD protect = PAGE_READONLY, access = FILE_MAP_READ;
73 
74  if (flags & RB_IO_BUFFER_READONLY) {
75  data->flags |= RB_IO_BUFFER_READONLY;
76  }
77  else {
78  protect = PAGE_READWRITE;
79  access = FILE_MAP_WRITE;
80  }
81 
82  HANDLE mapping = CreateFileMapping(file, NULL, protect, 0, 0, NULL);
83  if (!mapping) rb_sys_fail("io_buffer_map_descriptor:CreateFileMapping");
84 
85  if (flags & RB_IO_BUFFER_PRIVATE) {
86  access |= FILE_MAP_COPY;
87  data->flags |= RB_IO_BUFFER_PRIVATE;
88  } else {
89  // This buffer refers to external data.
90  data->flags |= RB_IO_BUFFER_EXTERNAL;
91  }
92 
93  void *base = MapViewOfFile(mapping, access, (DWORD)(offset >> 32), (DWORD)(offset & 0xFFFFFFFF), size);
94 
95  if (!base) {
96  CloseHandle(mapping);
97  rb_sys_fail("io_buffer_map_file:MapViewOfFile");
98  }
99 
100  data->mapping = mapping;
101 #else
102  int protect = PROT_READ, access = 0;
103 
104  if (flags & RB_IO_BUFFER_READONLY) {
105  data->flags |= RB_IO_BUFFER_READONLY;
106  }
107  else {
108  protect |= PROT_WRITE;
109  }
110 
111  if (flags & RB_IO_BUFFER_PRIVATE) {
112  data->flags |= RB_IO_BUFFER_PRIVATE;
113  }
114  else {
115  // This buffer refers to external data.
116  data->flags |= RB_IO_BUFFER_EXTERNAL;
117  access |= MAP_SHARED;
118  }
119 
120  void *base = mmap(NULL, size, protect, access, descriptor, offset);
121 
122  if (base == MAP_FAILED) {
123  rb_sys_fail("io_buffer_map_file:mmap");
124  }
125 #endif
126 
127  data->base = base;
128  data->size = size;
129 
130  data->flags |= RB_IO_BUFFER_MAPPED;
131 }
132 
133 static inline void
134 io_buffer_unmap(void* base, size_t size)
135 {
136 #ifdef _WIN32
137  VirtualFree(base, 0, MEM_RELEASE);
138 #else
139  munmap(base, size);
140 #endif
141 }
142 
143 static void
144 io_buffer_experimental(void)
145 {
146  static int warned = 0;
147 
148  if (warned) return;
149 
150  warned = 1;
151 
152  if (rb_warning_category_enabled_p(RB_WARN_CATEGORY_EXPERIMENTAL)) {
154  "IO::Buffer is experimental and both the Ruby and C interface may change in the future!"
155  );
156  }
157 }
158 
159 static void
160 io_buffer_zero(struct rb_io_buffer *data)
161 {
162  data->base = NULL;
163  data->size = 0;
164 #if defined(_WIN32)
165  data->mapping = NULL;
166 #endif
167  data->source = Qnil;
168 }
169 
170 static void
171 io_buffer_initialize(struct rb_io_buffer *data, void *base, size_t size, enum rb_io_buffer_flags flags, VALUE source)
172 {
173  if (base) {
174  // If we are provided a pointer, we use it.
175  }
176  else if (size) {
177  // If we are provided a non-zero size, we allocate it:
178  if (flags & RB_IO_BUFFER_INTERNAL) {
179  base = calloc(size, 1);
180  }
181  else if (flags & RB_IO_BUFFER_MAPPED) {
182  base = io_buffer_map_memory(size);
183  }
184 
185  if (!base) {
186  rb_raise(rb_eIOBufferAllocationError, "Could not allocate buffer!");
187  }
188  } else {
189  // Otherwise we don't do anything.
190  return;
191  }
192 
193  data->base = base;
194  data->size = size;
195  data->flags = flags;
196  data->source = source;
197 }
198 
199 static int
200 io_buffer_free(struct rb_io_buffer *data)
201 {
202  if (data->base) {
203  if (data->flags & RB_IO_BUFFER_INTERNAL) {
204  free(data->base);
205  }
206 
207  if (data->flags & RB_IO_BUFFER_MAPPED) {
208  io_buffer_unmap(data->base, data->size);
209  }
210 
211  // Previously we had this, but we found out due to the way GC works, we
212  // can't refer to any other Ruby objects here.
213  // if (RB_TYPE_P(data->source, T_STRING)) {
214  // rb_str_unlocktmp(data->source);
215  // }
216 
217  data->base = NULL;
218 
219 #if defined(_WIN32)
220  if (data->mapping) {
221  CloseHandle(data->mapping);
222  data->mapping = NULL;
223  }
224 #endif
225  data->size = 0;
226  data->flags = 0;
227  data->source = Qnil;
228 
229  return 1;
230  }
231 
232  return 0;
233 }
234 
235 void
236 rb_io_buffer_type_mark(void *_data)
237 {
238  struct rb_io_buffer *data = _data;
239  rb_gc_mark(data->source);
240 }
241 
242 void
243 rb_io_buffer_type_free(void *_data)
244 {
245  struct rb_io_buffer *data = _data;
246 
247  io_buffer_free(data);
248 
249  free(data);
250 }
251 
252 size_t
253 rb_io_buffer_type_size(const void *_data)
254 {
255  const struct rb_io_buffer *data = _data;
256  size_t total = sizeof(struct rb_io_buffer);
257 
258  if (data->flags) {
259  total += data->size;
260  }
261 
262  return total;
263 }
264 
265 static const rb_data_type_t rb_io_buffer_type = {
266  .wrap_struct_name = "IO::Buffer",
267  .function = {
268  .dmark = rb_io_buffer_type_mark,
269  .dfree = rb_io_buffer_type_free,
270  .dsize = rb_io_buffer_type_size,
271  },
272  .data = NULL,
273  .flags = RUBY_TYPED_FREE_IMMEDIATELY,
274 };
275 
276 VALUE
277 rb_io_buffer_type_allocate(VALUE self)
278 {
279  struct rb_io_buffer *data = NULL;
280  VALUE instance = TypedData_Make_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
281 
282  io_buffer_zero(data);
283 
284  return instance;
285 }
286 
287 static VALUE
288 io_buffer_for_make_instance(VALUE klass, VALUE string)
289 {
290  VALUE instance = rb_io_buffer_type_allocate(klass);
291 
292  struct rb_io_buffer *data = NULL;
293  TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, data);
294 
295  enum rb_io_buffer_flags flags = RB_IO_BUFFER_EXTERNAL;
296 
297  if (RB_OBJ_FROZEN(string))
298  flags |= RB_IO_BUFFER_READONLY;
299 
300  io_buffer_initialize(data, RSTRING_PTR(string), RSTRING_LEN(string), flags, string);
301 
302  return instance;
303 }
304 
306  VALUE klass;
307  VALUE string;
308  VALUE instance;
309 };
310 
311 static VALUE
312 io_buffer_for_yield_instance(VALUE _arguments) {
314 
315  rb_str_locktmp(arguments->string);
316 
317  arguments->instance = io_buffer_for_make_instance(arguments->klass, arguments->string);
318 
319  return rb_yield(arguments->instance);
320 }
321 
322 static VALUE
323 io_buffer_for_yield_instance_ensure(VALUE _arguments)
324 {
326 
327  if (arguments->instance != Qnil) {
328  rb_io_buffer_free(arguments->instance);
329  }
330 
331  rb_str_unlocktmp(arguments->string);
332 
333  return Qnil;
334 }
335 
336 /*
337  * call-seq:
338  * IO::Buffer.for(string) -> readonly io_buffer
339  * IO::Buffer.for(string) {|io_buffer| ... read/write io_buffer ...}
340  *
341  * Creates a IO::Buffer from the given string's memory. Without a block a
342  * frozen internal copy of the string is created efficiently and used as the
343  * buffer source. When a block is provided, the buffer is associated directly
344  * with the string's internal data and updating the buffer will update the
345  * string.
346  *
347  * Until #free is invoked on the buffer, either explicitly or via the garbage
348  * collector, the source string will be locked and cannot be modified.
349  *
350  * If the string is frozen, it will create a read-only buffer which cannot be
351  * modified.
352  *
353  * string = 'test'
354  * buffer = IO::Buffer.for(string)
355  * buffer.external? #=> true
356  *
357  * buffer.get_string(0, 1)
358  * # => "t"
359  * string
360  * # => "best"
361  *
362  * buffer.resize(100)
363  * # in `resize': Cannot resize external buffer! (IO::Buffer::AccessError)
364  *
365  * IO::Buffer.for(string) do |buffer|
366  * buffer.set_string("T")
367  * string
368  * # => "Test"
369  * end
370  */
371 VALUE
372 rb_io_buffer_type_for(VALUE klass, VALUE string)
373 {
374  StringValue(string);
375 
376  // If the string is frozen, both code paths are okay.
377  // If the string is not frozen, if a block is not given, it must be frozen.
378  if (rb_block_given_p()) {
379  struct io_buffer_for_yield_instance_arguments arguments = {
380  .klass = klass,
381  .string = string,
382  .instance = Qnil,
383  };
384 
385  return rb_ensure(io_buffer_for_yield_instance, (VALUE)&arguments, io_buffer_for_yield_instance_ensure, (VALUE)&arguments);
386  } else {
387  // This internally returns the source string if it's already frozen.
388  string = rb_str_tmp_frozen_acquire(string);
389  return io_buffer_for_make_instance(klass, string);
390  }
391 }
392 
393 VALUE
394 rb_io_buffer_new(void *base, size_t size, enum rb_io_buffer_flags flags)
395 {
396  VALUE instance = rb_io_buffer_type_allocate(rb_cIOBuffer);
397 
398  struct rb_io_buffer *data = NULL;
399  TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, data);
400 
401  io_buffer_initialize(data, base, size, flags, Qnil);
402 
403  return instance;
404 }
405 
406 VALUE
407 rb_io_buffer_map(VALUE io, size_t size, off_t offset, enum rb_io_buffer_flags flags)
408 {
409  io_buffer_experimental();
410 
411  VALUE instance = rb_io_buffer_type_allocate(rb_cIOBuffer);
412 
413  struct rb_io_buffer *data = NULL;
414  TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, data);
415 
416  int descriptor = rb_io_descriptor(io);
417 
418  io_buffer_map_file(data, descriptor, size, offset, flags);
419 
420  return instance;
421 }
422 
423 /*
424  * call-seq: IO::Buffer.map(file, [size, [offset, [flags]]]) -> io_buffer
425  *
426  * Create an IO::Buffer for reading from +file+ by memory-mapping the file.
427  * +file_io+ should be a +File+ instance, opened for reading.
428  *
429  * Optional +size+ and +offset+ of mapping can be specified.
430  *
431  * By default, the buffer would be immutable (read only); to create a writable
432  * mapping, you need to open a file in read-write mode, and explicitly pass
433  * +flags+ argument without IO::Buffer::IMMUTABLE.
434  *
435  * File.write('test.txt', 'test')
436  *
437  * buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::READONLY)
438  * # => #<IO::Buffer 0x00000001014a0000+4 MAPPED READONLY>
439  *
440  * buffer.readonly? # => true
441  *
442  * buffer.get_string
443  * # => "test"
444  *
445  * buffer.set_string('b', 0)
446  * # `set_string': Buffer is not writable! (IO::Buffer::AccessError)
447  *
448  * # create read/write mapping: length 4 bytes, offset 0, flags 0
449  * buffer = IO::Buffer.map(File.open('test.txt', 'r+'), 4, 0)
450  * buffer.set_string('b', 0)
451  * # => 1
452  *
453  * # Check it
454  * File.read('test.txt')
455  * # => "best"
456  *
457  * Note that some operating systems may not have cache coherency between mapped
458  * buffers and file reads.
459  *
460  */
461 static VALUE
462 io_buffer_map(int argc, VALUE *argv, VALUE klass)
463 {
464  if (argc < 1 || argc > 4) {
465  rb_error_arity(argc, 2, 4);
466  }
467 
468  // We might like to handle a string path?
469  VALUE io = argv[0];
470 
471  size_t size;
472  if (argc >= 2 && !RB_NIL_P(argv[1])) {
473  size = RB_NUM2SIZE(argv[1]);
474  }
475  else {
476  off_t file_size = rb_file_size(io);
477 
478  // Compiler can confirm that we handled file_size < 0 case:
479  if (file_size < 0) {
480  rb_raise(rb_eArgError, "Invalid negative file size!");
481  }
482  // Here, we assume that file_size is positive:
483  else if ((uintmax_t)file_size > SIZE_MAX) {
484  rb_raise(rb_eArgError, "File larger than address space!");
485  }
486  else {
487  // This conversion should be safe:
488  size = (size_t)file_size;
489  }
490  }
491 
492  off_t offset = 0;
493  if (argc >= 3) {
494  offset = NUM2OFFT(argv[2]);
495  }
496 
497  enum rb_io_buffer_flags flags = 0;
498  if (argc >= 4) {
499  flags = RB_NUM2UINT(argv[3]);
500  }
501 
502  return rb_io_buffer_map(io, size, offset, flags);
503 }
504 
505 // Compute the optimal allocation flags for a buffer of the given size.
506 static inline enum rb_io_buffer_flags
507 io_flags_for_size(size_t size)
508 {
509  if (size >= RUBY_IO_BUFFER_PAGE_SIZE) {
510  return RB_IO_BUFFER_MAPPED;
511  }
512 
513  return RB_IO_BUFFER_INTERNAL;
514 }
515 
516 /*
517  * call-seq: IO::Buffer.new([size = DEFAULT_SIZE, [flags = 0]]) -> io_buffer
518  *
519  * Create a new zero-filled IO::Buffer of +size+ bytes.
520  * By default, the buffer will be _internal_: directly allocated chunk
521  * of the memory. But if the requested +size+ is more than OS-specific
522  * IO::Bufer::PAGE_SIZE, the buffer would be allocated using the
523  * virtual memory mechanism (anonymous +mmap+ on Unix, +VirtualAlloc+
524  * on Windows). The behavior can be forced by passing IO::Buffer::MAPPED
525  * as a second parameter.
526  *
527  * Examples
528  *
529  * buffer = IO::Buffer.new(4)
530  * # =>
531  * # #<IO::Buffer 0x000055b34497ea10+4 INTERNAL>
532  * # 0x00000000 00 00 00 00 ....
533  *
534  * buffer.get_string(0, 1) # => "\x00"
535  *
536  * buffer.set_string("test")
537  * buffer
538  * # =>
539  * # #<IO::Buffer 0x000055b34497ea10+4 INTERNAL>
540  * # 0x00000000 74 65 73 74 test
541  *
542  */
543 VALUE
544 rb_io_buffer_initialize(int argc, VALUE *argv, VALUE self)
545 {
546  io_buffer_experimental();
547 
548  if (argc < 0 || argc > 2) {
549  rb_error_arity(argc, 0, 2);
550  }
551 
552  struct rb_io_buffer *data = NULL;
553  TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
554 
555  size_t size;
556 
557  if (argc > 0) {
558  size = RB_NUM2SIZE(argv[0]);
559  } else {
560  size = RUBY_IO_BUFFER_DEFAULT_SIZE;
561  }
562 
563  enum rb_io_buffer_flags flags = 0;
564  if (argc >= 2) {
565  flags = RB_NUM2UINT(argv[1]);
566  }
567  else {
568  flags |= io_flags_for_size(size);
569  }
570 
571  io_buffer_initialize(data, NULL, size, flags, Qnil);
572 
573  return self;
574 }
575 
576 static int
577 io_buffer_validate_slice(VALUE source, void *base, size_t size)
578 {
579  void *source_base = NULL;
580  size_t source_size = 0;
581 
582  if (RB_TYPE_P(source, T_STRING)) {
583  RSTRING_GETMEM(source, source_base, source_size);
584  }
585  else {
586  rb_io_buffer_get_bytes(source, &source_base, &source_size);
587  }
588 
589  // Source is invalid:
590  if (source_base == NULL) return 0;
591 
592  // Base is out of range:
593  if (base < source_base) return 0;
594 
595  const void *source_end = (char*)source_base + source_size;
596  const void *end = (char*)base + size;
597 
598  // End is out of range:
599  if (end > source_end) return 0;
600 
601  // It seems okay:
602  return 1;
603 }
604 
605 static int
606 io_buffer_validate(struct rb_io_buffer *data)
607 {
608  if (data->source != Qnil) {
609  // Only slices incur this overhead, unfortunately... better safe than sorry!
610  return io_buffer_validate_slice(data->source, data->base, data->size);
611  }
612  else {
613  return 1;
614  }
615 }
616 
617 /*
618  * call-seq: to_s -> string
619  *
620  * Short representation of the buffer. It includes the address, size and
621  * symbolic flags. This format is subject to change.
622  *
623  * puts IO::Buffer.new(4) # uses to_s internally
624  * # #<IO::Buffer 0x000055769f41b1a0+4 INTERNAL>
625  *
626  */
627 VALUE
628 rb_io_buffer_to_s(VALUE self)
629 {
630  struct rb_io_buffer *data = NULL;
631  TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
632 
633  VALUE result = rb_str_new_cstr("#<");
634 
635  rb_str_append(result, rb_class_name(CLASS_OF(self)));
636  rb_str_catf(result, " %p+%"PRIdSIZE, data->base, data->size);
637 
638  if (data->base == NULL) {
639  rb_str_cat2(result, " NULL");
640  }
641 
642  if (data->flags & RB_IO_BUFFER_EXTERNAL) {
643  rb_str_cat2(result, " EXTERNAL");
644  }
645 
646  if (data->flags & RB_IO_BUFFER_INTERNAL) {
647  rb_str_cat2(result, " INTERNAL");
648  }
649 
650  if (data->flags & RB_IO_BUFFER_MAPPED) {
651  rb_str_cat2(result, " MAPPED");
652  }
653 
654  if (data->flags & RB_IO_BUFFER_LOCKED) {
655  rb_str_cat2(result, " LOCKED");
656  }
657 
658  if (data->flags & RB_IO_BUFFER_READONLY) {
659  rb_str_cat2(result, " READONLY");
660  }
661 
662  if (data->source != Qnil) {
663  rb_str_cat2(result, " SLICE");
664  }
665 
666  if (!io_buffer_validate(data)) {
667  rb_str_cat2(result, " INVALID");
668  }
669 
670  return rb_str_cat2(result, ">");
671 }
672 
673 static VALUE
674 io_buffer_hexdump(VALUE string, size_t width, char *base, size_t size, int first)
675 {
676  char *text = alloca(width+1);
677  text[width] = '\0';
678 
679  for (size_t offset = 0; offset < size; offset += width) {
680  memset(text, '\0', width);
681  if (first) {
682  rb_str_catf(string, "0x%08zx ", offset);
683  first = 0;
684  } else {
685  rb_str_catf(string, "\n0x%08zx ", offset);
686  }
687 
688  for (size_t i = 0; i < width; i += 1) {
689  if (offset+i < size) {
690  unsigned char value = ((unsigned char*)base)[offset+i];
691 
692  if (value < 127 && isprint(value)) {
693  text[i] = (char)value;
694  }
695  else {
696  text[i] = '.';
697  }
698 
699  rb_str_catf(string, " %02x", value);
700  }
701  else {
702  rb_str_cat2(string, " ");
703  }
704  }
705 
706  rb_str_catf(string, " %s", text);
707  }
708 
709  return string;
710 }
711 
712 static VALUE
713 rb_io_buffer_hexdump(VALUE self)
714 {
715  struct rb_io_buffer *data = NULL;
716  TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
717 
718  VALUE result = Qnil;
719 
720  if (io_buffer_validate(data) && data->base) {
721  result = rb_str_buf_new(data->size*3 + (data->size/16)*12 + 1);
722 
723  io_buffer_hexdump(result, 16, data->base, data->size, 1);
724  }
725 
726  return result;
727 }
728 
729 VALUE
730 rb_io_buffer_inspect(VALUE self)
731 {
732  struct rb_io_buffer *data = NULL;
733  TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
734 
735  VALUE result = rb_io_buffer_to_s(self);
736 
737  if (io_buffer_validate(data)) {
738  // Limit the maximum size genearted by inspect.
739  if (data->size <= 256) {
740  io_buffer_hexdump(result, 16, data->base, data->size, 0);
741  }
742  }
743 
744  return result;
745 }
746 
747 /*
748  * call-seq: size -> integer
749  *
750  * Returns the size of the buffer that was explicitly set (on creation with ::new
751  * or on #resize), or deduced on buffer's creation from string or file.
752  *
753  */
754 VALUE
755 rb_io_buffer_size(VALUE self)
756 {
757  struct rb_io_buffer *data = NULL;
758  TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
759 
760  return SIZET2NUM(data->size);
761 }
762 
763 /*
764  * call-seq: valid? -> true or false
765  *
766  * Returns whether the buffer data is accessible.
767  *
768  * A buffer becomes invalid if it is a slice of another buffer which has been
769  * freed.
770  *
771  */
772 static VALUE
773 rb_io_buffer_valid_p(VALUE self)
774 {
775  struct rb_io_buffer *data = NULL;
776  TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
777 
778  return RBOOL(io_buffer_validate(data));
779 }
780 
781 /*
782  * call-seq: null? -> true or false
783  *
784  * If the buffer was freed with #free or was never allocated in the first
785  * place.
786  *
787  */
788 static VALUE
789 rb_io_buffer_null_p(VALUE self)
790 {
791  struct rb_io_buffer *data = NULL;
792  TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
793 
794  return RBOOL(data->base == NULL);
795 }
796 
797 /*
798  * call-seq: external? -> true or false
799  *
800  * If the buffer is _external_, meaning it references from memory which is not
801  * allocated or mapped by the buffer itself.
802  *
803  * A buffer created using ::for has an external reference to the string's
804  * memory.
805  *
806  * External buffer can't be resized.
807  *
808  */
809 static VALUE
810 rb_io_buffer_empty_p(VALUE self)
811 {
812  struct rb_io_buffer *data = NULL;
813  TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
814 
815  return RBOOL(data->size == 0);
816 }
817 
818 static VALUE
819 rb_io_buffer_external_p(VALUE self)
820 {
821  struct rb_io_buffer *data = NULL;
822  TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
823 
824  return RBOOL(data->flags & RB_IO_BUFFER_EXTERNAL);
825 }
826 
827 /*
828  * call-seq: internal? -> true or false
829  *
830  * If the buffer is _internal_, meaning it references memory allocated by the
831  * buffer itself.
832  *
833  * An internal buffer is not associated with any external memory (e.g. string)
834  * or file mapping.
835  *
836  * Internal buffers are created using ::new and is the default when the
837  * requested size is less than the IO::Buffer::PAGE_SIZE and it was not
838  * requested to be mapped on creation.
839  *
840  * Internal buffers can be resized, and such an operation will typically
841  * invalidate all slices, but not always.
842  *
843  */
844 static VALUE
845 rb_io_buffer_internal_p(VALUE self)
846 {
847  struct rb_io_buffer *data = NULL;
848  TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
849 
850  return RBOOL(data->flags & RB_IO_BUFFER_INTERNAL);
851 }
852 
853 /*
854  * call-seq: mapped? -> true or false
855  *
856  * If the buffer is _mapped_, meaning it references memory mapped by the
857  * buffer.
858  *
859  * Mapped buffers are either anonymous, if created by ::new with the
860  * IO::Buffer::MAPPED flag or if the size was at least IO::Buffer::PAGE_SIZE,
861  * or backed by a file if created with ::map.
862  *
863  * Mapped buffers can usually be resized, and such an operation will typically
864  * invalidate all slices, but not always.
865  *
866  */
867 static VALUE
868 rb_io_buffer_mapped_p(VALUE self)
869 {
870  struct rb_io_buffer *data = NULL;
871  TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
872 
873  return RBOOL(data->flags & RB_IO_BUFFER_MAPPED);
874 }
875 
876 /*
877  * call-seq: locked? -> true or false
878  *
879  * If the buffer is _locked_, meaning it is inside #locked block execution.
880  * Locked buffer can't be resized or freed, and another lock can't be acquired
881  * on it.
882  *
883  * Locking is not thread safe, but is a semantic used to ensure buffers don't
884  * move while being used by a system call.
885  *
886  * buffer.locked do
887  * buffer.write(io) # theoretical system call interface
888  * end
889  *
890  */
891 static VALUE
892 rb_io_buffer_locked_p(VALUE self)
893 {
894  struct rb_io_buffer *data = NULL;
895  TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
896 
897  return RBOOL(data->flags & RB_IO_BUFFER_LOCKED);
898 }
899 
900 /*
901  * call-seq: readonly? -> true or false
902  *
903  * If the buffer is _read only_, meaning the buffer cannot be modified using
904  * #set_value, #set_string or #copy and similar.
905  *
906  * Frozen strings and read-only files create read-only buffers.
907  *
908  */
909 int
910 rb_io_buffer_readonly_p(VALUE self)
911 {
912  struct rb_io_buffer *data = NULL;
913  TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
914 
915  return data->flags & RB_IO_BUFFER_READONLY;
916 }
917 
918 static VALUE
919 io_buffer_readonly_p(VALUE self)
920 {
921  return RBOOL(rb_io_buffer_readonly_p(self));
922 }
923 
924 VALUE
925 rb_io_buffer_lock(VALUE self)
926 {
927  struct rb_io_buffer *data = NULL;
928  TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
929 
930  if (data->flags & RB_IO_BUFFER_LOCKED) {
931  rb_raise(rb_eIOBufferLockedError, "Buffer already locked!");
932  }
933 
934  data->flags |= RB_IO_BUFFER_LOCKED;
935 
936  return self;
937 }
938 
939 VALUE
940 rb_io_buffer_unlock(VALUE self)
941 {
942  struct rb_io_buffer *data = NULL;
943  TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
944 
945  if (!(data->flags & RB_IO_BUFFER_LOCKED)) {
946  rb_raise(rb_eIOBufferLockedError, "Buffer not locked!");
947  }
948 
949  data->flags &= ~RB_IO_BUFFER_LOCKED;
950 
951  return self;
952 }
953 
954 int
955 rb_io_buffer_try_unlock(VALUE self)
956 {
957  struct rb_io_buffer *data = NULL;
958  TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
959 
960  if (data->flags & RB_IO_BUFFER_LOCKED) {
961  data->flags &= ~RB_IO_BUFFER_LOCKED;
962  return 1;
963  }
964 
965  return 0;
966 }
967 
968 /*
969  * call-seq: locked { ... }
970  *
971  * Allows to process a buffer in exclusive way, for concurrency-safety. While
972  * the block is performed, the buffer is considered locked, and no other code
973  * can enter the lock. Also, locked buffer can't be changed with #resize or
974  * #free.
975  *
976  * buffer = IO::Buffer.new(4)
977  * buffer.locked? #=> false
978  *
979  * Fiber.schedule do
980  * buffer.locked do
981  * buffer.write(io) # theoretical system call interface
982  * end
983  * end
984  *
985  * Fiber.schedule do
986  * # in `locked': Buffer already locked! (IO::Buffer::LockedError)
987  * buffer.locked do
988  * buffer.set_string(...)
989  * end
990  * end
991  *
992  * The following operations acquire a lock: #resize, #free.
993  *
994  * Locking is not thread safe. It is designed as a safety net around
995  * non-blocking system calls. You can only share a buffer between threads with
996  * appropriate synchronisation techniques.
997  */
998 VALUE
999 rb_io_buffer_locked(VALUE self)
1000 {
1001  struct rb_io_buffer *data = NULL;
1002  TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1003 
1004  if (data->flags & RB_IO_BUFFER_LOCKED) {
1005  rb_raise(rb_eIOBufferLockedError, "Buffer already locked!");
1006  }
1007 
1008  data->flags |= RB_IO_BUFFER_LOCKED;
1009 
1010  VALUE result = rb_yield(self);
1011 
1012  data->flags &= ~RB_IO_BUFFER_LOCKED;
1013 
1014  return result;
1015 }
1016 
1017 /*
1018  * call-seq: free -> self
1019  *
1020  * If the buffer references memory, release it back to the operating system.
1021  * * for a _mapped_ buffer (e.g. from file): unmap.
1022  * * for a buffer created from scratch: free memory.
1023  * * for a buffer created from string: undo the association.
1024  *
1025  * After the buffer is freed, no further operations can't be performed on it.
1026  *
1027  * buffer = IO::Buffer.for('test')
1028  * buffer.free
1029  * # => #<IO::Buffer 0x0000000000000000+0 NULL>
1030  *
1031  * buffer.get_value(:U8, 0)
1032  * # in `get_value': The buffer is not allocated! (IO::Buffer::AllocationError)
1033  *
1034  * buffer.get_string
1035  * # in `get_string': The buffer is not allocated! (IO::Buffer::AllocationError)
1036  *
1037  * buffer.null?
1038  * # => true
1039  *
1040  * You can resize a freed buffer to re-allocate it.
1041  *
1042  */
1043 VALUE
1044 rb_io_buffer_free(VALUE self)
1045 {
1046  struct rb_io_buffer *data = NULL;
1047  TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1048 
1049  if (data->flags & RB_IO_BUFFER_LOCKED) {
1050  rb_raise(rb_eIOBufferLockedError, "Buffer is locked!");
1051  }
1052 
1053  io_buffer_free(data);
1054 
1055  return self;
1056 }
1057 
1058 static inline void
1059 io_buffer_validate_range(struct rb_io_buffer *data, size_t offset, size_t length)
1060 {
1061  if (offset + length > data->size) {
1062  rb_raise(rb_eArgError, "Specified offset+length exceeds data size!");
1063  }
1064 }
1065 
1066 /*
1067  * call-seq: slice(offset, length) -> io_buffer
1068  *
1069  * Produce another IO::Buffer which is a slice (or view into) the current one
1070  * starting at +offset+ bytes and going for +length+ bytes.
1071  *
1072  * The slicing happens without copying of memory, and the slice keeps being
1073  * associated with the original buffer's source (string, or file), if any.
1074  *
1075  * Raises RuntimeError if the <tt>offset+length<tt> is out of the current
1076  * buffer's bounds.
1077  *
1078  * string = 'test'
1079  * buffer = IO::Buffer.for(string)
1080  *
1081  * slice = buffer.slice(1, 2)
1082  * # =>
1083  * # #<IO::Buffer 0x00007fc3d34ebc49+2 SLICE>
1084  * # 0x00000000 65 73 es
1085  *
1086  * # Put "o" into 0s position of the slice
1087  * slice.set_string('o', 0)
1088  * slice
1089  * # =>
1090  * # #<IO::Buffer 0x00007fc3d34ebc49+2 SLICE>
1091  * # 0x00000000 6f 73 os
1092  *
1093  *
1094  * # it is also visible at position 1 of the original buffer
1095  * buffer
1096  * # =>
1097  * # #<IO::Buffer 0x00007fc3d31e2d80+4 SLICE>
1098  * # 0x00000000 74 6f 73 74 tost
1099  *
1100  * # ...and original string
1101  * string
1102  * # => tost
1103  *
1104  */
1105 VALUE
1106 rb_io_buffer_slice(VALUE self, VALUE _offset, VALUE _length)
1107 {
1108  // TODO fail on negative offets/lengths.
1109  size_t offset = NUM2SIZET(_offset);
1110  size_t length = NUM2SIZET(_length);
1111 
1112  struct rb_io_buffer *data = NULL;
1113  TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1114 
1115  io_buffer_validate_range(data, offset, length);
1116 
1117  VALUE instance = rb_io_buffer_type_allocate(rb_class_of(self));
1118  struct rb_io_buffer *slice = NULL;
1119  TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, slice);
1120 
1121  slice->base = (char*)data->base + offset;
1122  slice->size = length;
1123 
1124  // The source should be the root buffer:
1125  if (data->source != Qnil)
1126  slice->source = data->source;
1127  else
1128  slice->source = self;
1129 
1130  return instance;
1131 }
1132 
1133 int rb_io_buffer_get_bytes(VALUE self, void **base, size_t *size)
1134 {
1135  struct rb_io_buffer *data = NULL;
1136  TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1137 
1138  if (io_buffer_validate(data)) {
1139  if (data->base) {
1140  *base = data->base;
1141  *size = data->size;
1142 
1143  return data->flags;
1144  }
1145  }
1146 
1147  *base = NULL;
1148  *size = 0;
1149 
1150  return 0;
1151 }
1152 
1153 static void
1154 io_buffer_get_bytes_for_writing(struct rb_io_buffer *data, void **base, size_t *size)
1155 {
1156  if (data->flags & RB_IO_BUFFER_READONLY) {
1157  rb_raise(rb_eIOBufferAccessError, "Buffer is not writable!");
1158  }
1159 
1160  if (!io_buffer_validate(data)) {
1161  rb_raise(rb_eIOBufferInvalidatedError, "Buffer is invalid!");
1162  }
1163 
1164  if (data->base) {
1165  *base = data->base;
1166  *size = data->size;
1167 
1168  return;
1169  }
1170 
1171  rb_raise(rb_eIOBufferAllocationError, "The buffer is not allocated!");
1172 }
1173 
1174 void
1175 rb_io_buffer_get_bytes_for_writing(VALUE self, void **base, size_t *size)
1176 {
1177  struct rb_io_buffer *data = NULL;
1178  TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1179 
1180  io_buffer_get_bytes_for_writing(data, base, size);
1181 }
1182 
1183 static void
1184 io_buffer_get_bytes_for_reading(struct rb_io_buffer *data, const void **base, size_t *size)
1185 {
1186  if (!io_buffer_validate(data)) {
1187  rb_raise(rb_eIOBufferInvalidatedError, "Buffer has been invalidated!");
1188  }
1189 
1190  if (data->base) {
1191  *base = data->base;
1192  *size = data->size;
1193 
1194  return;
1195  }
1196 
1197  rb_raise(rb_eIOBufferAllocationError, "The buffer is not allocated!");
1198 }
1199 
1200 void
1201 rb_io_buffer_get_bytes_for_reading(VALUE self, const void **base, size_t *size)
1202 {
1203  struct rb_io_buffer *data = NULL;
1204  TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1205 
1206  io_buffer_get_bytes_for_reading(data, base, size);
1207 }
1208 
1209 /*
1210  * call-seq: transfer -> new_io_buffer
1211  *
1212  * Transfers ownership to a new buffer, deallocating the current one.
1213  *
1214  * buffer = IO::Buffer.new('test')
1215  * other = buffer.transfer
1216  * other
1217  * # =>
1218  * # #<IO::Buffer 0x00007f136a15f7b0+4 SLICE>
1219  * # 0x00000000 74 65 73 74 test
1220  * buffer
1221  * # =>
1222  * # #<IO::Buffer 0x0000000000000000+0 NULL>
1223  * buffer.null?
1224  * # => true
1225  *
1226  */
1227 VALUE
1228 rb_io_buffer_transfer(VALUE self)
1229 {
1230  struct rb_io_buffer *data = NULL;
1231  TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1232 
1233  if (data->flags & RB_IO_BUFFER_LOCKED) {
1234  rb_raise(rb_eIOBufferLockedError, "Cannot transfer ownership of locked buffer!");
1235  }
1236 
1237  VALUE instance = rb_io_buffer_type_allocate(rb_class_of(self));
1238  struct rb_io_buffer *transferred;
1239  TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, transferred);
1240 
1241  *transferred = *data;
1242  io_buffer_zero(data);
1243 
1244  return instance;
1245 }
1246 
1247 static void
1248 io_buffer_resize_clear(struct rb_io_buffer *data, void* base, size_t size)
1249 {
1250  if (size > data->size) {
1251  memset((unsigned char*)base+data->size, 0, size - data->size);
1252  }
1253 }
1254 
1255 static void
1256 io_buffer_resize_copy(struct rb_io_buffer *data, size_t size)
1257 {
1258  // Slow path:
1259  struct rb_io_buffer resized;
1260  io_buffer_initialize(&resized, NULL, size, io_flags_for_size(size), Qnil);
1261 
1262  if (data->base) {
1263  size_t preserve = data->size;
1264  if (preserve > size) preserve = size;
1265  memcpy(resized.base, data->base, preserve);
1266 
1267  io_buffer_resize_clear(data, resized.base, size);
1268  }
1269 
1270  io_buffer_free(data);
1271  *data = resized;
1272 }
1273 
1274 void
1275 rb_io_buffer_resize(VALUE self, size_t size)
1276 {
1277  struct rb_io_buffer *data = NULL;
1278  TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1279 
1280  if (data->flags & RB_IO_BUFFER_LOCKED) {
1281  rb_raise(rb_eIOBufferLockedError, "Cannot resize locked buffer!");
1282  }
1283 
1284  if (data->base == NULL) {
1285  io_buffer_initialize(data, NULL, size, io_flags_for_size(size), Qnil);
1286  return;
1287  }
1288 
1289  if (data->flags & RB_IO_BUFFER_EXTERNAL) {
1290  rb_raise(rb_eIOBufferAccessError, "Cannot resize external buffer!");
1291  }
1292 
1293 #ifdef MREMAP_MAYMOVE
1294  if (data->flags & RB_IO_BUFFER_MAPPED) {
1295  void *base = mremap(data->base, data->size, size, MREMAP_MAYMOVE);
1296 
1297  if (base == MAP_FAILED) {
1298  rb_sys_fail("rb_io_buffer_resize:mremap");
1299  }
1300 
1301  io_buffer_resize_clear(data, base, size);
1302 
1303  data->base = base;
1304  data->size = size;
1305 
1306  return;
1307  }
1308 #endif
1309 
1310  if (data->flags & RB_IO_BUFFER_INTERNAL) {
1311  void *base = realloc(data->base, size);
1312 
1313  if (!base) {
1314  rb_sys_fail("rb_io_buffer_resize:realloc");
1315  }
1316 
1317  io_buffer_resize_clear(data, base, size);
1318 
1319  data->base = base;
1320  data->size = size;
1321 
1322  return;
1323  }
1324 
1325  io_buffer_resize_copy(data, size);
1326 }
1327 
1328 /*
1329  * call-seq: resize(new_size) -> self
1330  *
1331  * Resizes a buffer to a +new_size+ bytes, preserving its content.
1332  * Depending on the old and new size, the memory area associated with
1333  * the buffer might be either extended, or rellocated at different
1334  * address with content being copied.
1335  *
1336  * buffer = IO::Buffer.new(4)
1337  * buffer.set_string("test", 0)
1338  * buffer.resize(8) # resize to 8 bytes
1339  * # =>
1340  * # #<IO::Buffer 0x0000555f5d1a1630+8 INTERNAL>
1341  * # 0x00000000 74 65 73 74 00 00 00 00 test....
1342  *
1343  * External buffer (created with ::for), and locked buffer
1344  * can not be resized.
1345  *
1346  */
1347 static VALUE
1348 io_buffer_resize(VALUE self, VALUE size)
1349 {
1350  rb_io_buffer_resize(self, NUM2SIZET(size));
1351 
1352  return self;
1353 }
1354 
1355 /*
1356  * call-seq: <=>(other) -> true or false
1357  *
1358  * Buffers are compared by size and exact contents of the memory they are
1359  * referencing using +memcmp+.
1360  *
1361  */
1362 static VALUE
1363 rb_io_buffer_compare(VALUE self, VALUE other)
1364 {
1365  const void *ptr1, *ptr2;
1366  size_t size1, size2;
1367 
1368  rb_io_buffer_get_bytes_for_reading(self, &ptr1, &size1);
1369  rb_io_buffer_get_bytes_for_reading(other, &ptr2, &size2);
1370 
1371  if (size1 < size2) {
1372  return RB_INT2NUM(-1);
1373  }
1374 
1375  if (size1 > size2) {
1376  return RB_INT2NUM(1);
1377  }
1378 
1379  return RB_INT2NUM(memcmp(ptr1, ptr2, size1));
1380 }
1381 
1382 static void
1383 io_buffer_validate_type(size_t size, size_t offset)
1384 {
1385  if (offset > size) {
1386  rb_raise(rb_eArgError, "Type extends beyond end of buffer!");
1387  }
1388 }
1389 
1390 // Lower case: little endian.
1391 // Upper case: big endian (network endian).
1392 //
1393 // :U8 | unsigned 8-bit integer.
1394 // :S8 | signed 8-bit integer.
1395 //
1396 // :u16, :U16 | unsigned 16-bit integer.
1397 // :s16, :S16 | signed 16-bit integer.
1398 //
1399 // :u32, :U32 | unsigned 32-bit integer.
1400 // :s32, :S32 | signed 32-bit integer.
1401 //
1402 // :u64, :U64 | unsigned 64-bit integer.
1403 // :s64, :S64 | signed 64-bit integer.
1404 //
1405 // :f32, :F32 | 32-bit floating point number.
1406 // :f64, :F64 | 64-bit floating point number.
1407 
1408 #define ruby_swap8(value) value
1409 
1410 union swapf32 {
1411  uint32_t integral;
1412  float value;
1413 };
1414 
1415 static float
1416 ruby_swapf32(float value)
1417 {
1418  union swapf32 swap = {.value = value};
1419  swap.integral = ruby_swap32(swap.integral);
1420  return swap.value;
1421 }
1422 
1423 union swapf64 {
1424  uint64_t integral;
1425  double value;
1426 };
1427 
1428 static double
1429 ruby_swapf64(double value)
1430 {
1431  union swapf64 swap = {.value = value};
1432  swap.integral = ruby_swap64(swap.integral);
1433  return swap.value;
1434 }
1435 
1436 #define DECLARE_TYPE(name, type, endian, wrap, unwrap, swap) \
1437 static ID RB_IO_BUFFER_TYPE_##name; \
1438 \
1439 static VALUE \
1440 io_buffer_read_##name(const void* base, size_t size, size_t *offset) \
1441 { \
1442  io_buffer_validate_type(size, *offset + sizeof(type)); \
1443  type value; \
1444  memcpy(&value, (char*)base + *offset, sizeof(type)); \
1445  if (endian != RB_IO_BUFFER_HOST_ENDIAN) value = swap(value); \
1446  *offset += sizeof(type); \
1447  return wrap(value); \
1448 } \
1449 \
1450 static void \
1451 io_buffer_write_##name(const void* base, size_t size, size_t *offset, VALUE _value) \
1452 { \
1453  io_buffer_validate_type(size, *offset + sizeof(type)); \
1454  type value = unwrap(_value); \
1455  if (endian != RB_IO_BUFFER_HOST_ENDIAN) value = swap(value); \
1456  memcpy((char*)base + *offset, &value, sizeof(type)); \
1457  *offset += sizeof(type); \
1458 }
1459 
1460 DECLARE_TYPE(U8, uint8_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap8)
1461 DECLARE_TYPE(S8, int8_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap8)
1462 
1463 DECLARE_TYPE(u16, uint16_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap16)
1464 DECLARE_TYPE(U16, uint16_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap16)
1465 DECLARE_TYPE(s16, int16_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap16)
1466 DECLARE_TYPE(S16, int16_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap16)
1467 
1468 DECLARE_TYPE(u32, uint32_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap32)
1469 DECLARE_TYPE(U32, uint32_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap32)
1470 DECLARE_TYPE(s32, int32_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap32)
1471 DECLARE_TYPE(S32, int32_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap32)
1472 
1473 DECLARE_TYPE(u64, uint64_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_ULL2NUM, RB_NUM2ULL, ruby_swap64)
1474 DECLARE_TYPE(U64, uint64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_ULL2NUM, RB_NUM2ULL, ruby_swap64)
1475 DECLARE_TYPE(s64, int64_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_LL2NUM, RB_NUM2LL, ruby_swap64)
1476 DECLARE_TYPE(S64, int64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_LL2NUM, RB_NUM2LL, ruby_swap64)
1477 
1478 DECLARE_TYPE(f32, float, RB_IO_BUFFER_LITTLE_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf32)
1479 DECLARE_TYPE(F32, float, RB_IO_BUFFER_BIG_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf32)
1480 DECLARE_TYPE(f64, double, RB_IO_BUFFER_LITTLE_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf64)
1481 DECLARE_TYPE(F64, double, RB_IO_BUFFER_BIG_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf64)
1482 #undef DECLARE_TYPE
1483 
1484 VALUE
1485 rb_io_buffer_get_value(const void* base, size_t size, ID type, size_t offset)
1486 {
1487 #define READ_TYPE(name) if (type == RB_IO_BUFFER_TYPE_##name) return io_buffer_read_##name(base, size, &offset);
1488  READ_TYPE(U8)
1489  READ_TYPE(S8)
1490 
1491  READ_TYPE(u16)
1492  READ_TYPE(U16)
1493  READ_TYPE(s16)
1494  READ_TYPE(S16)
1495 
1496  READ_TYPE(u32)
1497  READ_TYPE(U32)
1498  READ_TYPE(s32)
1499  READ_TYPE(S32)
1500 
1501  READ_TYPE(u64)
1502  READ_TYPE(U64)
1503  READ_TYPE(s64)
1504  READ_TYPE(S64)
1505 
1506  READ_TYPE(f32)
1507  READ_TYPE(F32)
1508  READ_TYPE(f64)
1509  READ_TYPE(F64)
1510 #undef READ_TYPE
1511 
1512  rb_raise(rb_eArgError, "Invalid type name!");
1513 }
1514 
1515 /*
1516  * call-seq: get_value(type, offset) -> numeric
1517  *
1518  * Read from buffer a value of +type+ at +offset+. +type+ should be one
1519  * of symbols:
1520  *
1521  * * +:U8+: unsigned integer, 1 byte
1522  * * +:S8+: signed integer, 1 byte
1523  * * +:u16+: unsigned integer, 2 bytes, little-endian
1524  * * +:U16+: unsigned integer, 2 bytes, big-endian
1525  * * +:s16+: signed integer, 2 bytes, little-endian
1526  * * +:S16+: signed integer, 2 bytes, big-endian
1527  * * +:u32+: unsigned integer, 4 bytes, little-endian
1528  * * +:U32+: unsigned integer, 4 bytes, big-endian
1529  * * +:s32+: signed integer, 4 bytes, little-endian
1530  * * +:S32+: signed integer, 4 bytes, big-endian
1531  * * +:u64+: unsigned integer, 8 bytes, little-endian
1532  * * +:U64+: unsigned integer, 8 bytes, big-endian
1533  * * +:s64+: signed integer, 8 bytes, little-endian
1534  * * +:S64+: signed integer, 8 bytes, big-endian
1535  * * +:f32+: float, 4 bytes, little-endian
1536  * * +:F32+: float, 4 bytes, big-endian
1537  * * +:f64+: double, 8 bytes, little-endian
1538  * * +:F64+: double, 8 bytes, big-endian
1539  *
1540  * Example:
1541  *
1542  * string = [1.5].pack('f')
1543  * # => "\x00\x00\xC0?"
1544  * IO::Buffer.for(string).get_value(:f32, 0)
1545  * # => 1.5
1546  *
1547  */
1548 static VALUE
1549 io_buffer_get_value(VALUE self, VALUE type, VALUE _offset)
1550 {
1551  const void *base;
1552  size_t size;
1553  size_t offset = NUM2SIZET(_offset);
1554 
1555  rb_io_buffer_get_bytes_for_reading(self, &base, &size);
1556 
1557  return rb_io_buffer_get_value(base, size, RB_SYM2ID(type), offset);
1558 }
1559 
1560 void
1561 rb_io_buffer_set_value(const void* base, size_t size, ID type, size_t offset, VALUE value)
1562 {
1563 #define WRITE_TYPE(name) if (type == RB_IO_BUFFER_TYPE_##name) {io_buffer_write_##name(base, size, &offset, value); return;}
1564  WRITE_TYPE(U8)
1565  WRITE_TYPE(S8)
1566 
1567  WRITE_TYPE(u16)
1568  WRITE_TYPE(U16)
1569  WRITE_TYPE(s16)
1570  WRITE_TYPE(S16)
1571 
1572  WRITE_TYPE(u32)
1573  WRITE_TYPE(U32)
1574  WRITE_TYPE(s32)
1575  WRITE_TYPE(S32)
1576 
1577  WRITE_TYPE(u64)
1578  WRITE_TYPE(U64)
1579  WRITE_TYPE(s64)
1580  WRITE_TYPE(S64)
1581 
1582  WRITE_TYPE(f32)
1583  WRITE_TYPE(F32)
1584  WRITE_TYPE(f64)
1585  WRITE_TYPE(F64)
1586 #undef WRITE_TYPE
1587 
1588  rb_raise(rb_eArgError, "Invalid type name!");
1589 }
1590 
1591 /*
1592  * call-seq: set_value(type, offset, value) -> offset
1593  *
1594  * Write to a buffer a +value+ of +type+ at +offset+. +type+ should be one of
1595  * symbols described in #get_value.
1596  *
1597  * buffer = IO::Buffer.new(8)
1598  * # =>
1599  * # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
1600  * # 0x00000000 00 00 00 00 00 00 00 00
1601  * buffer.set_value(:U8, 1, 111)
1602  * # => 1
1603  * buffer
1604  * # =>
1605  * # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
1606  * # 0x00000000 00 6f 00 00 00 00 00 00 .o......
1607  *
1608  * Note that if the +type+ is integer and +value+ is Float, the implicit truncation is performed:
1609  *
1610  * buffer = IO::Buffer.new(8)
1611  * buffer.set_value(:U32, 0, 2.5)
1612  * buffer
1613  * # =>
1614  * # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
1615  * # 0x00000000 00 00 00 02 00 00 00 00
1616  * # ^^ the same as if we'd pass just integer 2
1617  */
1618 static VALUE
1619 io_buffer_set_value(VALUE self, VALUE type, VALUE _offset, VALUE value)
1620 {
1621  void *base;
1622  size_t size;
1623  size_t offset = NUM2SIZET(_offset);
1624 
1625  rb_io_buffer_get_bytes_for_writing(self, &base, &size);
1626 
1627  rb_io_buffer_set_value(base, size, RB_SYM2ID(type), offset, value);
1628 
1629  return SIZET2NUM(offset);
1630 }
1631 
1632 static void
1633 io_buffer_memcpy(struct rb_io_buffer *data, size_t offset, const void *source_base, size_t source_offset, size_t source_size, size_t length)
1634 {
1635  void *base;
1636  size_t size;
1637  io_buffer_get_bytes_for_writing(data, &base, &size);
1638 
1639  io_buffer_validate_range(data, offset, length);
1640 
1641  if (source_offset + length > source_size) {
1642  rb_raise(rb_eArgError, "The computed source range exceeds the size of the source!");
1643  }
1644 
1645  memcpy((unsigned char*)base+offset, (unsigned char*)source_base+source_offset, length);
1646 }
1647 
1648 // (offset, length, source_offset) -> length
1649 static VALUE
1650 io_buffer_copy_from(struct rb_io_buffer *data, const void *source_base, size_t source_size, int argc, VALUE *argv)
1651 {
1652  size_t offset;
1653  size_t length;
1654  size_t source_offset;
1655 
1656  // The offset we copy into the buffer:
1657  if (argc >= 1) {
1658  offset = NUM2SIZET(argv[0]);
1659  } else {
1660  offset = 0;
1661  }
1662 
1663  // The offset we start from within the string:
1664  if (argc >= 3) {
1665  source_offset = NUM2SIZET(argv[2]);
1666 
1667  if (source_offset > source_size) {
1668  rb_raise(rb_eArgError, "The given source offset is bigger than the source itself!");
1669  }
1670  } else {
1671  source_offset = 0;
1672  }
1673 
1674  // The length we are going to copy:
1675  if (argc >= 2 && !RB_NIL_P(argv[1])) {
1676  length = NUM2SIZET(argv[1]);
1677  } else {
1678  // Default to the source offset -> source size:
1679  length = source_size - source_offset;
1680  }
1681 
1682  io_buffer_memcpy(data, offset, source_base, source_offset, source_size, length);
1683 
1684  return SIZET2NUM(length);
1685 }
1686 
1687 /*
1688  * call-seq:
1689  * copy(source, [offset, [length, [source_offset]]]) -> size
1690  *
1691  * Efficiently copy data from a source IO::Buffer into the buffer,
1692  * at +offset+ using +memcpy+. For copying String instances, see #set_string.
1693  *
1694  * buffer = IO::Buffer.new(32)
1695  * # =>
1696  * # #<IO::Buffer 0x0000555f5ca22520+32 INTERNAL>
1697  * # 0x00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1698  * # 0x00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ *
1699  *
1700  * buffer.copy(IO::Buffer.for("test"), 8)
1701  * # => 4 -- size of data copied
1702  * buffer
1703  * # =>
1704  * # #<IO::Buffer 0x0000555f5cf8fe40+32 INTERNAL>
1705  * # 0x00000000 00 00 00 00 00 00 00 00 74 65 73 74 00 00 00 00 ........test....
1706  * # 0x00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ *
1707  *
1708  * #copy can be used to put data into strings associated with buffer:
1709  *
1710  * string= "data: "
1711  * # => "data: "
1712  * buffer = IO::Buffer.for(str)
1713  * buffer.copy(IO::Buffer.for("test"), 5)
1714  * # => 4
1715  * string
1716  * # => "data:test"
1717  *
1718  * Attempt to copy into a read-only buffer will fail:
1719  *
1720  * File.write('test.txt', 'test')
1721  * buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::READONLY)
1722  * buffer.copy(IO::Buffer.for("test"), 8)
1723  * # in `copy': Buffer is not writable! (IO::Buffer::AccessError)
1724  *
1725  * See ::map for details of creation of mutable file mappings, this will
1726  * work:
1727  *
1728  * buffer = IO::Buffer.map(File.open('test.txt', 'r+'))
1729  * buffer.copy("boom", 0)
1730  * # => 4
1731  * File.read('test.txt')
1732  * # => "boom"
1733  *
1734  * Attempt to copy the data which will need place outside of buffer's
1735  * bounds will fail:
1736  *
1737  * buffer = IO::Buffer.new(2)
1738  * buffer.copy('test', 0)
1739  * # in `copy': Specified offset+length exceeds source size! (ArgumentError)
1740  *
1741  */
1742 static VALUE
1743 io_buffer_copy(int argc, VALUE *argv, VALUE self)
1744 {
1745  if (argc < 1 || argc > 4) rb_error_arity(argc, 1, 4);
1746 
1747  struct rb_io_buffer *data = NULL;
1748  TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1749 
1750  VALUE source = argv[0];
1751  const void *source_base;
1752  size_t source_size;
1753 
1754  rb_io_buffer_get_bytes_for_reading(source, &source_base, &source_size);
1755 
1756  return io_buffer_copy_from(data, source_base, source_size, argc-1, argv+1);
1757 }
1758 
1759 /*
1760  * call-seq: get_string([offset, [length, [encoding]]]) -> string
1761  *
1762  * Read a chunk or all of the buffer into a string, in the specified
1763  * +encoding+. If no encoding is provided +Encoding::BINARY+ is used.
1764  *
1765  *
1766  * buffer = IO::Buffer.for('test')
1767  * buffer.get_string
1768  * # => "test"
1769  * buffer.get_string(2)
1770  * # => "st"
1771  * buffer.get_string(2, 1)
1772  * # => "s"
1773  *
1774  */
1775 static VALUE
1776 io_buffer_get_string(int argc, VALUE *argv, VALUE self)
1777 {
1778  if (argc > 3) rb_error_arity(argc, 0, 3);
1779 
1780  struct rb_io_buffer *data = NULL;
1781  TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1782 
1783  const void *base;
1784  size_t size;
1785  io_buffer_get_bytes_for_reading(data, &base, &size);
1786 
1787  size_t offset = 0;
1788  size_t length = size;
1789  rb_encoding *encoding = rb_ascii8bit_encoding();
1790 
1791  if (argc >= 1) {
1792  offset = NUM2SIZET(argv[0]);
1793  }
1794 
1795  if (argc >= 2 && !RB_NIL_P(argv[1])) {
1796  length = NUM2SIZET(argv[1]);
1797  } else {
1798  length = size - offset;
1799  }
1800 
1801  if (argc >= 3) {
1802  encoding = rb_find_encoding(argv[2]);
1803  }
1804 
1805  io_buffer_validate_range(data, offset, length);
1806 
1807  return rb_enc_str_new((const char*)base + offset, length, encoding);
1808 }
1809 
1810 static VALUE
1811 io_buffer_set_string(int argc, VALUE *argv, VALUE self)
1812 {
1813  if (argc < 1 || argc > 4) rb_error_arity(argc, 1, 4);
1814 
1815  struct rb_io_buffer *data = NULL;
1816  TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1817 
1818  VALUE string = rb_str_to_str(argv[0]);
1819 
1820  const void *source_base = RSTRING_PTR(string);
1821  size_t source_size = RSTRING_LEN(string);
1822 
1823  return io_buffer_copy_from(data, source_base, source_size, argc-1, argv+1);
1824 }
1825 
1826 void
1827 rb_io_buffer_clear(VALUE self, uint8_t value, size_t offset, size_t length)
1828 {
1829  void *base;
1830  size_t size;
1831 
1832  rb_io_buffer_get_bytes_for_writing(self, &base, &size);
1833 
1834  if (offset + length > size) {
1835  rb_raise(rb_eArgError, "The given offset + length out of bounds!");
1836  }
1837 
1838  memset((char*)base + offset, value, length);
1839 }
1840 
1841 /*
1842  * call-seq: clear(value = 0, [offset, [length]]) -> self
1843  *
1844  * Fill buffer with +value+, starting with +offset+ and going for +length+
1845  * bytes.
1846  *
1847  * buffer = IO::Buffer.for('test')
1848  * # =>
1849  * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
1850  * # 0x00000000 74 65 73 74 test
1851  *
1852  * buffer.clear
1853  * # =>
1854  * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
1855  * # 0x00000000 00 00 00 00 ....
1856  *
1857  * buf.clear(1) # fill with 1
1858  * # =>
1859  * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
1860  * # 0x00000000 01 01 01 01 ....
1861  *
1862  * buffer.clear(2, 1, 2) # fill with 2, starting from offset 1, for 2 bytes
1863  * # =>
1864  * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
1865  * # 0x00000000 01 02 02 01 ....
1866  *
1867  * buffer.clear(2, 1) # fill with 2, starting from offset 1
1868  * # =>
1869  * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
1870  * # 0x00000000 01 02 02 02 ....
1871  *
1872  */
1873 static VALUE
1874 io_buffer_clear(int argc, VALUE *argv, VALUE self)
1875 {
1876  if (argc > 3) rb_error_arity(argc, 0, 3);
1877 
1878  struct rb_io_buffer *data = NULL;
1879  TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1880 
1881  uint8_t value = 0;
1882  if (argc >= 1) {
1883  value = NUM2UINT(argv[0]);
1884  }
1885 
1886  size_t offset = 0;
1887  if (argc >= 2) {
1888  offset = NUM2SIZET(argv[1]);
1889  }
1890 
1891  size_t length;
1892  if (argc >= 3) {
1893  length = NUM2SIZET(argv[2]);
1894  } else {
1895  length = data->size - offset;
1896  }
1897 
1898  rb_io_buffer_clear(self, value, offset, length);
1899 
1900  return self;
1901 }
1902 
1903 static
1904 size_t io_buffer_default_size(size_t page_size) {
1905  // Platform agnostic default size, based on empirical performance observation:
1906  const size_t platform_agnostic_default_size = 64*1024;
1907 
1908  // Allow user to specify custom default buffer size:
1909  const char *default_size = getenv("RUBY_IO_BUFFER_DEFAULT_SIZE");
1910  if (default_size) {
1911  // For the purpose of setting a default size, 2^31 is an acceptable maximum:
1912  int value = atoi(default_size);
1913 
1914  // assuming sizeof(int) <= sizeof(size_t)
1915  if (value > 0) {
1916  return value;
1917  }
1918  }
1919 
1920  if (platform_agnostic_default_size < page_size) {
1921  return page_size;
1922  }
1923 
1924  return platform_agnostic_default_size;
1925 }
1926 
1927 VALUE
1928 rb_io_buffer_read(VALUE self, VALUE io, size_t length)
1929 {
1930  VALUE scheduler = rb_fiber_scheduler_current();
1931  if (scheduler != Qnil) {
1932  VALUE result = rb_fiber_scheduler_io_read(scheduler, io, self, length);
1933 
1934  if (result != Qundef) {
1935  return result;
1936  }
1937  }
1938 
1939  struct rb_io_buffer *data = NULL;
1940  TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1941 
1942  io_buffer_validate_range(data, 0, length);
1943 
1944  int descriptor = rb_io_descriptor(io);
1945 
1946  void * base;
1947  size_t size;
1948  io_buffer_get_bytes_for_writing(data, &base, &size);
1949 
1950  ssize_t result = read(descriptor, base, size);
1951 
1952  return rb_fiber_scheduler_io_result(result, errno);
1953 }
1954 
1955 static VALUE
1956 io_buffer_read(VALUE self, VALUE io, VALUE length)
1957 {
1958  return rb_io_buffer_read(self, io, RB_NUM2SIZE(length));
1959 }
1960 
1961 VALUE
1962 rb_io_buffer_pread(VALUE self, VALUE io, size_t length, off_t offset)
1963 {
1964  VALUE scheduler = rb_fiber_scheduler_current();
1965  if (scheduler != Qnil) {
1966  VALUE result = rb_fiber_scheduler_io_pread(scheduler, io, self, length, offset);
1967 
1968  if (result != Qundef) {
1969  return result;
1970  }
1971  }
1972 
1973  struct rb_io_buffer *data = NULL;
1974  TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1975 
1976  io_buffer_validate_range(data, 0, length);
1977 
1978  int descriptor = rb_io_descriptor(io);
1979 
1980  void * base;
1981  size_t size;
1982  io_buffer_get_bytes_for_writing(data, &base, &size);
1983 
1984 #if defined(HAVE_PREAD)
1985  ssize_t result = pread(descriptor, base, size, offset);
1986 #else
1987  // This emulation is not thread safe, but the GVL means it's unlikely to be a problem.
1988  off_t current_offset = lseek(descriptor, 0, SEEK_CUR);
1989  if (current_offset == (off_t)-1)
1990  return rb_fiber_scheduler_io_result(-1, errno);
1991 
1992  if (lseek(descriptor, offset, SEEK_SET) == (off_t)-1)
1993  return rb_fiber_scheduler_io_result(-1, errno);
1994 
1995  ssize_t result = read(descriptor, base, size);
1996 
1997  if (lseek(descriptor, current_offset, SEEK_SET) == (off_t)-1)
1998  return rb_fiber_scheduler_io_result(-1, errno);
1999 #endif
2000 
2001  return rb_fiber_scheduler_io_result(result, errno);
2002 }
2003 
2004 static VALUE
2005 io_buffer_pread(VALUE self, VALUE io, VALUE length, VALUE offset)
2006 {
2007  return rb_io_buffer_pread(self, io, RB_NUM2SIZE(length), NUM2OFFT(offset));
2008 }
2009 
2010 VALUE
2011 rb_io_buffer_write(VALUE self, VALUE io, size_t length)
2012 {
2013  VALUE scheduler = rb_fiber_scheduler_current();
2014  if (scheduler != Qnil) {
2015  VALUE result = rb_fiber_scheduler_io_write(scheduler, io, self, length);
2016 
2017  if (result != Qundef) {
2018  return result;
2019  }
2020  }
2021 
2022  struct rb_io_buffer *data = NULL;
2023  TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
2024 
2025  io_buffer_validate_range(data, 0, length);
2026 
2027  int descriptor = rb_io_descriptor(io);
2028 
2029  const void * base;
2030  size_t size;
2031  io_buffer_get_bytes_for_reading(data, &base, &size);
2032 
2033  ssize_t result = write(descriptor, base, length);
2034 
2035  return rb_fiber_scheduler_io_result(result, errno);
2036 }
2037 
2038 static VALUE
2039 io_buffer_write(VALUE self, VALUE io, VALUE length)
2040 {
2041  return rb_io_buffer_write(self, io, RB_NUM2SIZE(length));
2042 }
2043 
2044 VALUE
2045 rb_io_buffer_pwrite(VALUE self, VALUE io, size_t length, off_t offset)
2046 {
2047  VALUE scheduler = rb_fiber_scheduler_current();
2048  if (scheduler != Qnil) {
2049  VALUE result = rb_fiber_scheduler_io_pwrite(scheduler, io, self, length, OFFT2NUM(offset));
2050 
2051  if (result != Qundef) {
2052  return result;
2053  }
2054  }
2055 
2056  struct rb_io_buffer *data = NULL;
2057  TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
2058 
2059  io_buffer_validate_range(data, 0, length);
2060 
2061  int descriptor = rb_io_descriptor(io);
2062 
2063  const void * base;
2064  size_t size;
2065  io_buffer_get_bytes_for_reading(data, &base, &size);
2066 
2067 #if defined(HAVE_PWRITE)
2068  ssize_t result = pwrite(descriptor, base, length, offset);
2069 #else
2070  // This emulation is not thread safe, but the GVL means it's unlikely to be a problem.
2071  off_t current_offset = lseek(descriptor, 0, SEEK_CUR);
2072  if (current_offset == (off_t)-1)
2073  return rb_fiber_scheduler_io_result(-1, errno);
2074 
2075  if (lseek(descriptor, offset, SEEK_SET) == (off_t)-1)
2076  return rb_fiber_scheduler_io_result(-1, errno);
2077 
2078  ssize_t result = write(descriptor, base, length);
2079 
2080  if (lseek(descriptor, current_offset, SEEK_SET) == (off_t)-1)
2081  return rb_fiber_scheduler_io_result(-1, errno);
2082 #endif
2083 
2084  return rb_fiber_scheduler_io_result(result, errno);
2085 }
2086 
2087 static VALUE
2088 io_buffer_pwrite(VALUE self, VALUE io, VALUE length, VALUE offset)
2089 {
2090  return rb_io_buffer_pwrite(self, io, RB_NUM2SIZE(length), NUM2OFFT(offset));
2091 }
2092 
2093 /*
2094  * Document-class: IO::Buffer
2095  *
2096  * IO::Buffer is a low-level efficient buffer for input/output. There are three
2097  * ways of using buffer:
2098  *
2099  * * Create an empty buffer with ::new, fill it with data using #copy or
2100  * #set_value, #set_string, get data with #get_string;
2101  * * Create a buffer mapped to some string with ::for, then it could be used
2102  * both for reading with #get_string or #get_value, and writing (writing will
2103  * change the source string, too);
2104  * * Create a buffer mapped to some file with ::map, then it could be used for
2105  * reading and writing the underlying file.
2106  *
2107  * Interaction with string and file memory is performed by efficient low-level
2108  * C mechanisms like `memcpy`.
2109  *
2110  * The class is meant to be an utility for implementing more high-level mechanisms
2111  * like Fiber::SchedulerInterface#io_read and Fiber::SchedulerInterface#io_write.
2112  *
2113  * <b>Examples of usage:</b>
2114  *
2115  * Empty buffer:
2116  *
2117  * buffer = IO::Buffer.new(8) # create empty 8-byte buffer
2118  * # =>
2119  * # #<IO::Buffer 0x0000555f5d1a5c50+8 INTERNAL>
2120  * # ...
2121  * buffer
2122  * # =>
2123  * # <IO::Buffer 0x0000555f5d156ab0+8 INTERNAL>
2124  * # 0x00000000 00 00 00 00 00 00 00 00
2125  * buffer.set_string('test', 2) # put there bytes of the "test" string, starting from offset 2
2126  * # => 4
2127  * buffer.get_string # get the result
2128  * # => "\x00\x00test\x00\x00"
2129  *
2130  * \Buffer from string:
2131  *
2132  * string = 'data'
2133  * buffer = IO::Buffer.for(str)
2134  * # =>
2135  * # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
2136  * # ...
2137  * buffer
2138  * # =>
2139  * # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
2140  * # 0x00000000 64 61 74 61 data
2141  *
2142  * buffer.get_string(2) # read content starting from offset 2
2143  * # => "ta"
2144  * buffer.set_string('---', 1) # write content, starting from offset 1
2145  * # => 3
2146  * buffer
2147  * # =>
2148  * # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
2149  * # 0x00000000 64 2d 2d 2d d---
2150  * string # original string changed, too
2151  * # => "d---"
2152  *
2153  * \Buffer from file:
2154  *
2155  * File.write('test.txt', 'test data')
2156  * # => 9
2157  * buffer = IO::Buffer.map(File.open('test.txt'))
2158  * # =>
2159  * # #<IO::Buffer 0x00007f3f0768c000+9 MAPPED IMMUTABLE>
2160  * # ...
2161  * buffer.get_string(5, 2) # read 2 bytes, starting from offset 5
2162  * # => "da"
2163  * buffer.set_string('---', 1) # attempt to write
2164  * # in `set_string': Buffer is not writable! (IO::Buffer::AccessError)
2165  *
2166  * # To create writable file-mapped buffer
2167  * # Open file for read-write, pass size, offset, and flags=0
2168  * buffer = IO::Buffer.map(File.open('test.txt', 'r+'), 9, 0, 0)
2169  * buffer.set_string('---', 1)
2170  * # => 3 -- bytes written
2171  * File.read('test.txt')
2172  * # => "t--- data"
2173  *
2174  * <b>The class is experimental and the interface is subject to change.</b>
2175  */
2176 void
2177 Init_IO_Buffer(void)
2178 {
2179  rb_cIOBuffer = rb_define_class_under(rb_cIO, "Buffer", rb_cObject);
2180  rb_eIOBufferLockedError = rb_define_class_under(rb_cIOBuffer, "LockedError", rb_eRuntimeError);
2181  rb_eIOBufferAllocationError = rb_define_class_under(rb_cIOBuffer, "AllocationError", rb_eRuntimeError);
2182  rb_eIOBufferAccessError = rb_define_class_under(rb_cIOBuffer, "AccessError", rb_eRuntimeError);
2183  rb_eIOBufferInvalidatedError = rb_define_class_under(rb_cIOBuffer, "InvalidatedError", rb_eRuntimeError);
2184 
2185  rb_define_alloc_func(rb_cIOBuffer, rb_io_buffer_type_allocate);
2186  rb_define_singleton_method(rb_cIOBuffer, "for", rb_io_buffer_type_for, 1);
2187 
2188 #ifdef _WIN32
2189  SYSTEM_INFO info;
2190  GetSystemInfo(&info);
2191  RUBY_IO_BUFFER_PAGE_SIZE = info.dwPageSize;
2192 #else /* not WIN32 */
2193  RUBY_IO_BUFFER_PAGE_SIZE = sysconf(_SC_PAGESIZE);
2194 #endif
2195 
2196  RUBY_IO_BUFFER_DEFAULT_SIZE = io_buffer_default_size(RUBY_IO_BUFFER_PAGE_SIZE);
2197 
2198  // Efficient sizing of mapped buffers:
2199  rb_define_const(rb_cIOBuffer, "PAGE_SIZE", SIZET2NUM(RUBY_IO_BUFFER_PAGE_SIZE));
2200  rb_define_const(rb_cIOBuffer, "DEFAULT_SIZE", SIZET2NUM(RUBY_IO_BUFFER_DEFAULT_SIZE));
2201 
2202  rb_define_singleton_method(rb_cIOBuffer, "map", io_buffer_map, -1);
2203 
2204  // General use:
2205  rb_define_method(rb_cIOBuffer, "initialize", rb_io_buffer_initialize, -1);
2206  rb_define_method(rb_cIOBuffer, "inspect", rb_io_buffer_inspect, 0);
2207  rb_define_method(rb_cIOBuffer, "hexdump", rb_io_buffer_hexdump, 0);
2208  rb_define_method(rb_cIOBuffer, "to_s", rb_io_buffer_to_s, 0);
2209  rb_define_method(rb_cIOBuffer, "size", rb_io_buffer_size, 0);
2210  rb_define_method(rb_cIOBuffer, "valid?", rb_io_buffer_valid_p, 0);
2211 
2212  // Ownership:
2213  rb_define_method(rb_cIOBuffer, "transfer", rb_io_buffer_transfer, 0);
2214 
2215  // Flags:
2216  rb_define_const(rb_cIOBuffer, "EXTERNAL", RB_INT2NUM(RB_IO_BUFFER_EXTERNAL));
2217  rb_define_const(rb_cIOBuffer, "INTERNAL", RB_INT2NUM(RB_IO_BUFFER_INTERNAL));
2218  rb_define_const(rb_cIOBuffer, "MAPPED", RB_INT2NUM(RB_IO_BUFFER_MAPPED));
2219  rb_define_const(rb_cIOBuffer, "LOCKED", RB_INT2NUM(RB_IO_BUFFER_LOCKED));
2220  rb_define_const(rb_cIOBuffer, "PRIVATE", RB_INT2NUM(RB_IO_BUFFER_PRIVATE));
2221  rb_define_const(rb_cIOBuffer, "READONLY", RB_INT2NUM(RB_IO_BUFFER_READONLY));
2222 
2223  // Endian:
2224  rb_define_const(rb_cIOBuffer, "LITTLE_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_LITTLE_ENDIAN));
2225  rb_define_const(rb_cIOBuffer, "BIG_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_BIG_ENDIAN));
2226  rb_define_const(rb_cIOBuffer, "HOST_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_HOST_ENDIAN));
2227  rb_define_const(rb_cIOBuffer, "NETWORK_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_NETWORK_ENDIAN));
2228 
2229  rb_define_method(rb_cIOBuffer, "null?", rb_io_buffer_null_p, 0);
2230  rb_define_method(rb_cIOBuffer, "empty?", rb_io_buffer_empty_p, 0);
2231  rb_define_method(rb_cIOBuffer, "external?", rb_io_buffer_external_p, 0);
2232  rb_define_method(rb_cIOBuffer, "internal?", rb_io_buffer_internal_p, 0);
2233  rb_define_method(rb_cIOBuffer, "mapped?", rb_io_buffer_mapped_p, 0);
2234  rb_define_method(rb_cIOBuffer, "locked?", rb_io_buffer_locked_p, 0);
2235  rb_define_method(rb_cIOBuffer, "readonly?", io_buffer_readonly_p, 0);
2236 
2237  // Locking to prevent changes while using pointer:
2238  // rb_define_method(rb_cIOBuffer, "lock", rb_io_buffer_lock, 0);
2239  // rb_define_method(rb_cIOBuffer, "unlock", rb_io_buffer_unlock, 0);
2240  rb_define_method(rb_cIOBuffer, "locked", rb_io_buffer_locked, 0);
2241 
2242  // Manipulation:
2243  rb_define_method(rb_cIOBuffer, "slice", rb_io_buffer_slice, 2);
2244  rb_define_method(rb_cIOBuffer, "<=>", rb_io_buffer_compare, 1);
2245  rb_define_method(rb_cIOBuffer, "resize", io_buffer_resize, 1);
2246  rb_define_method(rb_cIOBuffer, "clear", io_buffer_clear, -1);
2247  rb_define_method(rb_cIOBuffer, "free", rb_io_buffer_free, 0);
2248 
2249  rb_include_module(rb_cIOBuffer, rb_mComparable);
2250 
2251 #define DEFINE_TYPE(name) RB_IO_BUFFER_TYPE_##name = rb_intern_const(#name)
2252  DEFINE_TYPE(U8); DEFINE_TYPE(S8);
2253  DEFINE_TYPE(u16); DEFINE_TYPE(U16); DEFINE_TYPE(s16); DEFINE_TYPE(S16);
2254  DEFINE_TYPE(u32); DEFINE_TYPE(U32); DEFINE_TYPE(s32); DEFINE_TYPE(S32);
2255  DEFINE_TYPE(u64); DEFINE_TYPE(U64); DEFINE_TYPE(s64); DEFINE_TYPE(S64);
2256  DEFINE_TYPE(f32); DEFINE_TYPE(F32); DEFINE_TYPE(f64); DEFINE_TYPE(F64);
2257 #undef DEFINE_TYPE
2258 
2259  // Data access:
2260  rb_define_method(rb_cIOBuffer, "get_value", io_buffer_get_value, 2);
2261  rb_define_method(rb_cIOBuffer, "set_value", io_buffer_set_value, 3);
2262 
2263  rb_define_method(rb_cIOBuffer, "copy", io_buffer_copy, -1);
2264 
2265  rb_define_method(rb_cIOBuffer, "get_string", io_buffer_get_string, -1);
2266  rb_define_method(rb_cIOBuffer, "set_string", io_buffer_set_string, -1);
2267 
2268  // IO operations:
2269  rb_define_method(rb_cIOBuffer, "read", io_buffer_read, 2);
2270  rb_define_method(rb_cIOBuffer, "pread", io_buffer_pread, 3);
2271  rb_define_method(rb_cIOBuffer, "write", io_buffer_write, 2);
2272  rb_define_method(rb_cIOBuffer, "pwrite", io_buffer_pwrite, 3);
2273 }
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
Definition: cxxanyargs.hpp:685
static bool RB_OBJ_FROZEN(VALUE obj)
Checks if an object is frozen.
Definition: fl_type.h:927
void rb_include_module(VALUE klass, VALUE module)
Includes a module to a class.
Definition: class.c:1043
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition: class.c:869
void rb_define_method(VALUE klass, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a method.
Definition: class.c:1914
int rb_block_given_p(void)
Determines if the current method is given a block.
Definition: eval.c:854
#define T_STRING
Old name of RUBY_T_STRING.
Definition: value_type.h:78
#define Qundef
Old name of RUBY_Qundef.
#define CLASS_OF
Old name of rb_class_of.
Definition: globals.h:203
#define SIZET2NUM
Old name of RB_SIZE2NUM.
Definition: size_t.h:62
#define NUM2UINT
Old name of RB_NUM2UINT.
Definition: int.h:45
#define NUM2DBL
Old name of rb_num2dbl.
Definition: double.h:27
#define Qnil
Old name of RUBY_Qnil.
#define DBL2NUM
Old name of rb_float_new.
Definition: double.h:29
#define NUM2SIZET
Old name of RB_NUM2SIZE.
Definition: size_t.h:61
void rb_category_warn(rb_warning_category_t category, const char *fmt,...)
Identical to rb_category_warning(), except it reports always regardless of runtime -W flag.
Definition: error.c:428
void rb_raise(VALUE exc, const char *fmt,...)
Exception entry point.
Definition: error.c:3025
void rb_sys_fail(const char *mesg)
Converts a C errno into a Ruby exception, then raises it.
Definition: error.c:3149
VALUE rb_eRuntimeError
RuntimeError exception.
Definition: error.c:1097
VALUE rb_eArgError
ArgumentError exception.
Definition: error.c:1100
VALUE rb_ensure(VALUE(*b_proc)(VALUE), VALUE data1, VALUE(*e_proc)(VALUE), VALUE data2)
An equivalent to ensure clause.
Definition: eval.c:983
@ RB_WARN_CATEGORY_EXPERIMENTAL
Warning is for experimental features.
Definition: error.h:51
VALUE rb_cIO
IO class.
Definition: io.c:185
static VALUE rb_class_of(VALUE obj)
Object to class mapping function.
Definition: globals.h:172
VALUE rb_mComparable
Comparable module.
Definition: compar.c:19
rb_encoding * rb_find_encoding(VALUE obj)
Identical to rb_to_encoding_index(), except the return type.
Definition: encoding.c:336
rb_encoding * rb_ascii8bit_encoding(void)
Queries the encoding that represents ASCII-8BIT a.k.a.
Definition: encoding.c:1515
VALUE rb_enc_str_new(const char *ptr, long len, rb_encoding *enc)
Identical to rb_enc_str_new(), except it additionally takes an encoding.
Definition: string.c:940
off_t rb_file_size(VALUE file)
Queries the file size of the given file.
Definition: file.c:2559
void rb_gc_mark(VALUE obj)
Marks an object.
Definition: gc.c:6775
VALUE rb_str_append(VALUE dst, VALUE src)
Identical to rb_str_buf_append(), except it converts the right hand side before concatenating.
Definition: string.c:3317
VALUE rb_str_cat2(VALUE, const char *)
Just another name of rb_str_cat_cstr.
VALUE rb_str_locktmp(VALUE str)
Obtains a "temporary lock" of the string.
VALUE rb_str_unlocktmp(VALUE str)
Releases a lock formerly obtained by rb_str_locktmp().
Definition: string.c:3022
VALUE rb_str_new_cstr(const char *ptr)
Identical to rb_str_new(), except it assumes the passed pointer is a pointer to a C string.
Definition: string.c:952
VALUE rb_str_buf_new(long capa)
Allocates a "string buffer".
Definition: string.c:1506
VALUE rb_class_name(VALUE obj)
Queries the name of the given object's class.
Definition: variable.c:294
void rb_define_alloc_func(VALUE klass, rb_alloc_func_t func)
Sets the allocator function of a class.
#define RB_SYM2ID
Just another name of rb_sym2id.
Definition: symbol.h:43
void rb_define_const(VALUE klass, const char *name, VALUE val)
Defines a Ruby level constant under a namespace.
Definition: variable.c:3253
int rb_io_descriptor(VALUE io)
Returns an integer representing the numeric file descriptor for io.
Definition: io.c:2650
#define RB_NUM2INT
Just another name of rb_num2int_inline.
Definition: int.h:38
#define RB_UINT2NUM
Just another name of rb_uint2num_inline.
Definition: int.h:39
#define RB_INT2NUM
Just another name of rb_int2num_inline.
Definition: int.h:37
static unsigned int RB_NUM2UINT(VALUE x)
Converts an instance of rb_cNumeric into C's unsigned int.
Definition: int.h:185
VALUE rb_str_catf(VALUE dst, const char *fmt,...)
Identical to rb_sprintf(), except it renders the output to the specified object rather than creating ...
Definition: sprintf.c:1241
#define RB_LL2NUM
Just another name of rb_ll2num_inline.
Definition: long_long.h:28
#define RB_ULL2NUM
Just another name of rb_ull2num_inline.
Definition: long_long.h:29
#define RB_NUM2ULL
Just another name of rb_num2ull_inline.
Definition: long_long.h:33
#define RB_NUM2LL
Just another name of rb_num2ll_inline.
Definition: long_long.h:32
VALUE rb_yield(VALUE val)
Yields the block.
Definition: vm_eval.c:1357
VALUE type(ANYARGS)
ANYARGS-ed function type.
Definition: cxxanyargs.hpp:56
#define OFFT2NUM
Converts a C's off_t into an instance of rb_cInteger.
Definition: off_t.h:33
#define NUM2OFFT
Converts an instance of rb_cNumeric into C's off_t.
Definition: off_t.h:44
#define StringValue(v)
Ensures that the parameter object is a String.
Definition: rstring.h:72
static char * RSTRING_PTR(VALUE str)
Queries the contents pointer of the string.
Definition: rstring.h:497
#define RSTRING_GETMEM(str, ptrvar, lenvar)
Convenient macro to obtain the contents and length at once.
Definition: rstring.h:573
static long RSTRING_LEN(VALUE str)
Queries the length of the string.
Definition: rstring.h:483
VALUE rb_str_to_str(VALUE obj)
Identical to rb_check_string_type(), except it raises exceptions in case of conversion failures.
Definition: string.c:1584
#define TypedData_Get_Struct(obj, type, data_type, sval)
Obtains a C struct from inside of a wrapper Ruby object.
Definition: rtypeddata.h:507
#define TypedData_Make_Struct(klass, type, data_type, sval)
Identical to TypedData_Wrap_Struct, except it allocates a new data region internally instead of takin...
Definition: rtypeddata.h:489
Scheduler APIs.
VALUE rb_fiber_scheduler_current(void)
Identical to rb_fiber_scheduler_get(), except it also returns RUBY_Qnil in case of a blocking fiber.
Definition: scheduler.c:126
static VALUE rb_fiber_scheduler_io_result(ssize_t result, int error)
Wrap a ssize_t and int errno into a single VALUE.
Definition: scheduler.h:46
VALUE rb_fiber_scheduler_io_read(VALUE scheduler, VALUE io, VALUE buffer, size_t length)
Nonblocking read from the passed IO.
Definition: scheduler.c:235
VALUE rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, VALUE buffer, size_t length, off_t offset)
Nonblocking read from the passed IO at the specified offset.
Definition: scheduler.c:245
VALUE rb_fiber_scheduler_io_write(VALUE scheduler, VALUE io, VALUE buffer, size_t length)
Nonblocking write to the passed IO.
Definition: scheduler.c:255
VALUE rb_fiber_scheduler_io_pwrite(VALUE scheduler, VALUE io, VALUE buffer, size_t length, off_t offset)
Nonblocking write to the passed IO at the specified offset.
Definition: scheduler.c:265
#define RB_NUM2SIZE
Converts an instance of rb_cInteger into C's size_t.
Definition: size_t.h:47
static bool RB_NIL_P(VALUE obj)
Checks if the given object is nil.
This is the struct that holds necessary info for a struct.
Definition: rtypeddata.h:190
const char * wrap_struct_name
Name of structs of this kind.
Definition: rtypeddata.h:197
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
Definition: value.h:52
uintptr_t VALUE
Type that represents a Ruby object.
Definition: value.h:40
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.
Definition: value_type.h:375