Class: Snow::Memory

Inherits:
Data
  • Object
show all
Defined in:
lib/snow-data/memory.rb,
ext/snow-data/snow-data.c

Overview

A class for representing blocks of memory. You can either allocate memory using ::malloc or wrap existing blocks of memory using ::new or ::wrap (or ::__wrap__ if a subclass has extended ::wrap and ::new).

If wrapping an existing block of memory, note that the Memory object does not take ownership of the block. As such, freeing it is the responsibility of its allocator. Only a block allocated by ::malloc will be freed by a Memory object, either by calling #free! or when the GC collects the object.

When subclassing Memory, which is fine to do, you’ll likely want to override Memory::new and possibly Memory::wrap. It is also possible to override Memory::malloc, but it’s recommended you never do this. If you do, keep in mind that __malloc__ exists as an alias of malloc and you must never try to override it, as this could result in very strange behavior. Similarly, new is aliased as both wrap and __wrap__, the latter of which you should must never override either. Again, it may result in undesirable behavior, crashes, and angry responses to any issues you create on GitHub as a result.

Constant Summary collapse

HAS_ALLOCA =

Whether or not __alloca__ is available (and ergo any other variant of it). You may check this instead of calling respond_to? if you wish.

self.respond_to?(:__alloca__)

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.alloca(size) {|memory| ... } ⇒ Object .__alloca__(size) {|memory| ... } ⇒ Object

Allocates size bytes on the stack and yields it to a block before for use, then returns the result of the block. Outside of the block, any use of the yielded memory’s address is considered undefined behavior and may lead to crashes or worse. The yielded memory is only valid for the duration of the block. If you want to preserve the memory after use, either duplicate the memory block or realloc! the memory, both of which will copy it from the stack to the heap.

The alignment of the yielded memory is always the size of a pointer.

Because of how downright evil alloca is in some situations, it is disabled by default. You must explicitly install snow-data with the –allow-alloca flag to use this function. If you’re not certain whether alloca is available for a particular installation, then you should check if the Memory class responds to it first.

if Snow::Memory.respond_to?(:__alloca__)
  Snow::Memory.__alloca__(64) { |mem|
    # ...
  }
end

Subclasses may override alloca to provide a predefined size argument, but subclasses must never override __alloca__. It is considered bad form to implement __alloca__ using heap allocation for installations that don’t provide it.

Overloads:

  • .alloca(size) {|memory| ... } ⇒ Object

    Yields:

    • (memory)
  • .__alloca__(size) {|memory| ... } ⇒ Object

    Yields:

    • (memory)


1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
# File 'ext/snow-data/snow-data.c', line 1491

static VALUE sd_memory_alloca(VALUE self, VALUE sd_size)
{
  VALUE result = Qnil;
  void *stack_memory = NULL;
  struct RData *block_data;
  size_t size = NUM2SIZET(sd_size);
  VALUE block;

  rb_need_block();

  if (size == 0) {
    rb_raise(rb_eRangeError, "Size of block must be 1 or more -- zero-byte"
      " blocks are not permitted.");
  }

  stack_memory = alloca(size);
  if (stack_memory == NULL) {
    rb_raise(rb_eNoMemError,
      "Failed to allocated %zu bytes via alloca",
      size);
  }

  block = sd_wrap_memory(self, stack_memory, size, SIZEOF_VOIDP, SD_DO_NOT_FREE_MEMORY);
  block_data = RDATA(block);
  result = rb_yield(block);

  /*
    If the block hasn't been realloc!'d or freed, free it now if it hasn't been
    frozen for some reason.
   */
  if (block_data->data == stack_memory && !block_data->dfree && !OBJ_FROZEN(block)) {
    sd_memory_force_free(block);
  }

  return result;
}

.malloc(size, alignment = nil) ⇒ Memory .__malloc__(size, alignment = nil) ⇒ Memory Also known as: malloc, []

Allocates a new block with the given size and alignment and returns it. If no alignment is specified, it defaults to Snow::Memory::SIZEOF_VOID_POINTER.

Raises a RangeError if either size is zero or alignment is not a power of two.

If a subclass overrides ::malloc, which is a bad idea and should not be done, then it is aliased as ::__malloc__ as well. Subclasses must never override ::__malloc__.

Overloads:

  • .malloc(size, alignment = nil) ⇒ Memory

    Returns:

  • .__malloc__(size, alignment = nil) ⇒ Memory

    Returns:



1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
# File 'ext/snow-data/snow-data.c', line 1429

static VALUE sd_memory_malloc(int argc, VALUE *argv, VALUE self)
{
  VALUE sd_size;
  VALUE sd_alignment;
  size_t alignment;
  size_t size;
  void *data;
  VALUE memory;

  rb_scan_args(argc, argv, "11", &sd_size, &sd_alignment);

  /* Get size and alignment */
  size = NUM2SIZET(sd_size);
  alignment = RTEST(sd_alignment) ? NUM2SIZET(sd_alignment) : sizeof(void *);
  if (!is_power_of_two(alignment)) {
    rb_raise(rb_eRangeError, "Alignment must be a power of two -- %zu is not a"
      " power of two", alignment);
  } else if (size < 1) {
    rb_raise(rb_eRangeError, "Size of block must be 1 or more -- zero-byte"
      " blocks are not permitted");
  }

  /* Allocate block */
  data = com_malloc(size, alignment);
  memory = sd_wrap_memory(self, data, size, alignment, SD_FREE_MEMORY);

  return memory;
}

.new(address, size, alignment = SIZEOF_VOID_POINTER) ⇒ Memory .wrap(address, size, alignment = SIZEOF_VOID_POINTER) ⇒ Memory .__wrap__(address, size, alignment = SIZEOF_VOID_POINTER) ⇒ Memory Also known as: new, wrap

Creates a new Memory object that wraps an existing pointer. Alignment is optional and defaults to the size of a pointer (Memory::SIZEOF_VOID_POINTER).

Size must be greater than zero. Zero-sized blocks are not permitted as they render most memory functionality useless and make it very difficult to ensure nothing bad is happening when you do bad things with snow-data. Because, let’s be honest with ourselves for a moment, everyone using this gem? They’re bad people. They’re very bad people.

Note that Memory objects created with this method will not attempt to free the memory they wrap, as they did not allocate it and so do not own it. If an address held by a Memory object is invalid, the Memory object is also implicitly invalid as well, though there is no way for it to check this. You are responsible for freeing any memory not allocated through ::malloc and Memory subclasses.

It is an ArgumentError to provide a size of zero, nil, or false. It is also an ArgumentError to provide a NULL (zero) address.

If a subclass overrides ::new, it is also aliased as ::wrap and ::__wrap__. Subclasses may override ::wrap or ::new but must never override ::__wrap__.

Overloads:

  • .new(address, size, alignment = SIZEOF_VOID_POINTER) ⇒ Memory

    Returns:

  • .wrap(address, size, alignment = SIZEOF_VOID_POINTER) ⇒ Memory

    Returns:

  • .__wrap__(address, size, alignment = SIZEOF_VOID_POINTER) ⇒ Memory

    Returns:



1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
# File 'ext/snow-data/snow-data.c', line 1376

static VALUE sd_memory_new(int argc, VALUE *argv, VALUE self)
{
  VALUE sd_size;
  VALUE sd_address;
  VALUE sd_alignment;
  VALUE memory = Qnil;
  void *address;
  size_t size;
  size_t alignment;

  rb_scan_args(argc, argv, "21", &sd_address, &sd_size, &sd_alignment);

  address = (void *)SD_NUM_TO_INTPTR_T(sd_address);
  size = 0;
  alignment = SIZEOF_VOIDP;

  if (address == NULL) {
    rb_raise(rb_eArgError, "Address is NULL (%p).", address);
  }

  if (RTEST(sd_size)) {
    size = NUM2SIZET(sd_size);
  } else {
    rb_raise(rb_eArgError, "Block size is false or nil");
  }

  if (!size) {
    rb_raise(rb_eArgError, "Block size must be 1 or greater");
  }

  if (RTEST(sd_alignment)) {
    alignment = NUM2SIZET(sd_alignment);
  }

  memory = sd_wrap_memory(self, address, size, alignment, SD_DO_NOT_FREE_MEMORY);

  return memory;
}

.align_size(size_or_offset, alignment = nil) ⇒ Integer

Aligns a given size or offset to a specific alignment. If no alignment is provided, it defaults to the size of a pointer on the architecture the extension was compiled for.

See Snow::Memory::SIZEOF_VOID_POINTER for the size of a pointer.

Raises a RangeError if the alignment is not a power of two. In this case, 1 is considered a valid power of two.

Returns:

  • (Integer)


1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
# File 'ext/snow-data/snow-data.c', line 1874

static VALUE sd_align_size(int argc, VALUE *argv, VALUE self)
{
  VALUE sd_alignment;
  VALUE sd_size;
  size_t alignment = sizeof(void *);
  rb_scan_args(argc, argv, "11", &sd_size, &sd_alignment);
  if (RTEST(sd_alignment)) {
    alignment = NUM2SIZET(sd_alignment);
    if (!is_power_of_two(alignment)) {
      rb_raise(rb_eRangeError, "Alignment must be a power of two -- %zu is not"
        " a power of two", alignment);
    }
  }
  return SIZET2NUM(align_size(NUM2SIZET(sd_size), alignment));
}

Instance Method Details

#==(other) ⇒ Object

Returns whether this and another block are equal in terms of their properties – that is, whether they have the address and bytesize. If true, the objects refer to teh same block of memory. If false, they might still overlap, refer to different chunks of memory, one might be null, etc.



79
80
81
82
83
84
85
86
87
# File 'lib/snow-data/memory.rb', line 79

def ==(other)
  return false unless other

  if other.kind_of?(::Snow::Memory) || (other.respond_to?(:address) && other.respond_to?(:bytesize))
    self.address == other.address && self.bytesize == other.bytesize
  else
    false
  end
end

#addressInteger

Gets the address of this memory block as an Integer.

Returns:

  • (Integer)


1856
1857
1858
1859
# File 'ext/snow-data/snow-data.c', line 1856

static VALUE sd_memory_address(VALUE self)
{
  return SD_UINTPTR_T_TO_NUM((uintptr_t)RDATA(self)->data);
}

#alignmentObject

The alignment in bytes of a memory block.



60
61
62
# File 'lib/snow-data/memory.rb', line 60

def alignment
  @__alignment__
end

#bytesizeObject

The size in bytes of a memory block.



52
53
54
# File 'lib/snow-data/memory.rb', line 52

def bytesize
  @__bytesize__
end

#copy!(source, destination_offset = nil, source_offset = nil, byte_size = nil) ⇒ self

Copies byte_size bytes from an offset in the source data to an offset into the receiver (the destination).

If either offset is nil, they default to zero.

If the byte_size is nil, it defaults to the receiver’s #bytesize.

If the source responds to #bytesize and the source’s bytesize is smaller than the size given, the source’s size is used instead of the specified or default size.

The source pointer does not have its bounds checked, as this isn’t possible for all cases. Instead, you must ensure that your source offset and byte size are both within range of the source data.

For those curious, under the hood, this uses memmove, not memcpy. So, it is possible to copy overlapping regions of memory, but it isn’t guaranteed to be as fast as a simple memcpy. Either way, if this is a concern for you, you probably shouldn’t be using Ruby.

Exceptions

  • If attempting to copy into a region that is outside the bounds of the receiver will raise a RangeError.

  • If either the receiver or the source address is NULL, it will raise an ArgumentError.

  • If the source object is neither a Data object (or a subclass thereof) or a Numerical address, it raises a TypeError.

Returns:

  • (self)


1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
# File 'ext/snow-data/snow-data.c', line 1658

static VALUE sd_memory_copy(int argc, VALUE *argv, VALUE self)
{
  VALUE sd_source;
  VALUE sd_destination_offset;
  VALUE sd_source_offset;
  VALUE sd_byte_size;
  struct RData *self_data;
  const uint8_t *source_pointer;
  uint8_t *destination_pointer;
  size_t source_offset;
  size_t destination_offset;
  size_t byte_size;
  size_t self_byte_size;
  int source_is_data = 0;

  sd_check_null_block(self);
  rb_check_frozen(self);

  rb_scan_args(argc, argv, "13",
    &sd_source,
    &sd_destination_offset,
    &sd_source_offset,
    &sd_byte_size);

  /*
    By default, try to get an address from the object, if possible. Then use
    that address and don't extract it from the Data object or what have you.
   */
  if (rb_obj_respond_to(sd_source, kSD_ID_ADDRESS, 0)) {
    VALUE source_address = rb_funcall2(sd_source, kSD_ID_ADDRESS, 0, 0);
    if (RTEST(rb_obj_is_kind_of(source_address, rb_cNumeric))) {
      source_pointer = (uint8_t *)SD_NUM_TO_INTPTR_T(source_address);
      goto sd_memory_copy_skip_data_check;
    }
  }

  if (RTEST(rb_obj_is_kind_of(sd_source, rb_cData))) {
    /* Otherwise extract a pointer from the object if it's a Data object */
    const struct RData *source_data = RDATA(sd_source);
    source_pointer = ((const uint8_t *)source_data->data);
    source_is_data = 1;
  } else if (RTEST(rb_obj_is_kind_of(sd_source, rb_cNumeric))) {
    /* Otherwise, if it's a Numeric, try to convert what is assumed to be an
      address to a pointer */
    source_pointer = (uint8_t *)SD_NUM_TO_INTPTR_T(sd_source);
  } else {
    rb_raise(rb_eTypeError,
      "Source object must be type of numeric (address) or Data- got %s",
      rb_obj_classname(sd_source));
  }

sd_memory_copy_skip_data_check: /* skip from address check */
  self_data = RDATA(self);

  /*
    Check if the source pointer is NULL -- error if it is (the destination
    pointer is checked by sd_check_null_block above).
   */
  if (source_pointer == NULL) {
    rb_raise(rb_eArgError, "Source pointer is NULL");
  }

  /* Grab data from ruby values and offset the source pointer. */
  source_offset       = RTEST(sd_source_offset) ? NUM2SIZET(sd_source_offset) : 0;
  destination_offset  = RTEST(sd_destination_offset) ? NUM2SIZET(sd_destination_offset) : 0;
  destination_pointer = (uint8_t *)self_data->data + destination_offset;
  self_byte_size      = NUM2SIZET(rb_ivar_get(self, kSD_IVAR_BYTESIZE));
  source_pointer      += source_offset;

  if (self_byte_size == 0) {
    /*
      If self is a zero-length block, do not try to copy. It's just not sane to
      attempt it here since we can't do any bounds checking, even if the bounds
      might be arbitrarily specified (in which case I'd just be shooting myself
      in the foot if I did that and trying to circumvent the anti-foot-shot
      protections).
     */
    rb_raise(rb_eRuntimeError, "self.bytesize == 0 -- cannot safely copy to this block");
  } else if (!RTEST(sd_byte_size)) {
    /*
      If not copy size is specified, use self_byte_size and just try to cram as
      much in there as is feasible. Truncate self_byte_size as needed for the
      offset.
     */
    byte_size = self_byte_size - destination_offset;
    #ifdef SD_WARN_ON_IMPLICIT_COPY_SIZE
    if (!source_is_data) {
      rb_warning(
        "Copying %zu bytes from non-Data memory address %p without explicit size",
        byte_size,
        source_pointer);
    }
    #endif
  } else {
    /* User-specified size */
    byte_size = NUM2SIZET(sd_byte_size);
  }

  /*
    If the source responds to bytesize, check if the copy is within bounds for
    the source. If it's out of bounds, raise a RangeError, otherwise optionally
    emit a warning that bounds checking doesn't work for this source.
   */
  if (rb_obj_respond_to(sd_source, kSD_ID_BYTESIZE, 0)) {
    size_t source_size = NUM2SIZET(rb_funcall2(sd_source, kSD_ID_BYTESIZE, 0, 0));
    if (source_offset > source_size
        || (source_offset + byte_size) > source_size
        || (source_offset + byte_size) < source_offset) {
      rb_raise(rb_eRangeError, "Attempt to copy out of source bounds");
    }
  }
  #ifdef SD_WARN_ON_NO_BYTESIZE_METHOD
  else if (source_is_data) {
    rb_warning(
      "Copying from Data object pointer %p that does not respond to #bytesize"
      " -- this operation is not bounds-checked.",
      source_pointer);
  }
  #endif

  if ((destination_offset + byte_size) > self_byte_size
      || (destination_offset + byte_size) < destination_offset) {
    rb_raise(rb_eRangeError,
      "Offset %zu with byte size %zu is out of bounds of self",
      destination_offset,
      byte_size);
  }

  #ifdef SD_VERBOSE_COPY_LOG
  /*
    Emit some debugging info just in case things go completely haywire and you
    really need to know what's going on.
   */
  fprintf(stderr,
    "# copy! ----------------------------------------\n"
    "#  destination_pointer = %p"             "\n"
    "#  source_pointer      = %p"             "\n"
    "#  destination_offset  = %zu"            "\n"
    "#  source_offset       = %zu"            "\n"
    "#  byte_size           = %zu"            "\n"
    "#  self_byte_size      = %zu"            "\n"
    "#  source.class        = %s"             "\n"
    "#  self.class          = %s"             "\n"
    "# --------------------------------------- /copy!\n",
    destination_pointer - destination_offset,
    source_pointer - source_offset,
    destination_offset,
    source_offset,
    byte_size,
    self_byte_size,
    rb_obj_classname(sd_source),
    rb_obj_classname(self));
  #endif

  /* And skip a copy if we can */
  if (byte_size == 0 || source_pointer == destination_pointer) {
    return self;
  }

  memmove(destination_pointer, source_pointer, byte_size);

  return self;
}

#dupObject

Creates a new block of memory with the same class, size, and alignment; copies the receiver’s data to the new block; and returns the new block.



103
104
105
106
# File 'lib/snow-data/memory.rb', line 103

def dup
  new_self = self.class.__malloc__(self.bytesize, self.alignment)
  new_self.copy!(self, 0, 0, self.bytesize)
end

#free!self

Frees any memory owned by the object. This is a convenience function for when you want to free the memory ahead of the object being collected by the GC.

Returns:

  • (self)


1618
1619
1620
1621
1622
1623
# File 'ext/snow-data/snow-data.c', line 1618

static VALUE sd_memory_free(VALUE self)
{
  rb_check_frozen(self);
  sd_memory_force_free(self);
  return self;
}

#get_char(offset) ⇒ String

Reads a char from the offset into the memory block and returns it. The offset is not bounds-checked and it is possible to read outside of bounds, which is considered undefined behavior and may crash or do other horrible things.

Returns:

  • (String)


1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
# File 'ext/snow-data/snow-data.c', line 1101

static VALUE sd_get_char(VALUE self, VALUE sd_offset)
{
  typedef char conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  value = *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset);
  return SD_CHAR_TO_NUM(value);
}

#get_double(offset) ⇒ Object

Reads a double from the offset into the memory block and returns it. The offset is not bounds-checked and it is possible to read outside of bounds, which is considered undefined behavior and may crash or do other horrible things.



906
907
908
909
910
911
912
913
914
915
# File 'ext/snow-data/snow-data.c', line 906

static VALUE sd_get_double(VALUE self, VALUE sd_offset)
{
  typedef double conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  value = *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset);
  return SD_DOUBLE_TO_NUM(value);
}

#get_float(offset) ⇒ Float

Reads a float from the offset into the memory block and returns it. The offset is not bounds-checked and it is possible to read outside of bounds, which is considered undefined behavior and may crash or do other horrible things.

Returns:

  • (Float)


867
868
869
870
871
872
873
874
875
876
# File 'ext/snow-data/snow-data.c', line 867

static VALUE sd_get_float(VALUE self, VALUE sd_offset)
{
  typedef float conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  value = *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset);
  return SD_FLOAT_TO_NUM(value);
}

#get_int(offset) ⇒ Integer

Reads a int from the offset into the memory block and returns it. The offset is not bounds-checked and it is possible to read outside of bounds, which is considered undefined behavior and may crash or do other horrible things.

Returns:

  • (Integer)


945
946
947
948
949
950
951
952
953
954
# File 'ext/snow-data/snow-data.c', line 945

static VALUE sd_get_int(VALUE self, VALUE sd_offset)
{
  typedef int conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  value = *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset);
  return SD_INT_TO_NUM(value);
}

#get_int16(offset) ⇒ Object

Reads a int16_t from the offset into the memory block and returns it. The offset is not bounds-checked and it is possible to read outside of bounds, which is considered undefined behavior and may crash or do other horrible things.



282
283
284
285
286
287
288
289
290
291
# File 'ext/snow-data/snow-data.c', line 282

static VALUE sd_get_int16(VALUE self, VALUE sd_offset)
{
  typedef int16_t conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  value = *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset);
  return SD_INT16_TO_NUM(value);
}

#get_int32(offset) ⇒ Object

Reads a int32_t from the offset into the memory block and returns it. The offset is not bounds-checked and it is possible to read outside of bounds, which is considered undefined behavior and may crash or do other horrible things.



321
322
323
324
325
326
327
328
329
330
# File 'ext/snow-data/snow-data.c', line 321

static VALUE sd_get_int32(VALUE self, VALUE sd_offset)
{
  typedef int32_t conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  value = *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset);
  return SD_INT32_TO_NUM(value);
}

#get_int64(offset) ⇒ Object

Reads a int64_t from the offset into the memory block and returns it. The offset is not bounds-checked and it is possible to read outside of bounds, which is considered undefined behavior and may crash or do other horrible things.



360
361
362
363
364
365
366
367
368
369
# File 'ext/snow-data/snow-data.c', line 360

static VALUE sd_get_int64(VALUE self, VALUE sd_offset)
{
  typedef int64_t conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  value = *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset);
  return SD_INT64_TO_NUM(value);
}

#get_int8(offset) ⇒ Object

Reads a int8_t from the offset into the memory block and returns it. The offset is not bounds-checked and it is possible to read outside of bounds, which is considered undefined behavior and may crash or do other horrible things.



243
244
245
246
247
248
249
250
251
252
# File 'ext/snow-data/snow-data.c', line 243

static VALUE sd_get_int8(VALUE self, VALUE sd_offset)
{
  typedef int8_t conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  value = *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset);
  return SD_INT8_TO_NUM(value);
}

#get_intptr_t(offset) ⇒ Object

Reads a intptr_t from the offset into the memory block and returns it. The offset is not bounds-checked and it is possible to read outside of bounds, which is considered undefined behavior and may crash or do other horrible things.



633
634
635
636
637
638
639
640
641
642
# File 'ext/snow-data/snow-data.c', line 633

static VALUE sd_get_intptr_t(VALUE self, VALUE sd_offset)
{
  typedef intptr_t conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  value = *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset);
  return SD_INTPTR_T_TO_NUM(value);
}

#get_long(offset) ⇒ Integer

Reads a long from the offset into the memory block and returns it. The offset is not bounds-checked and it is possible to read outside of bounds, which is considered undefined behavior and may crash or do other horrible things.

Returns:

  • (Integer)


711
712
713
714
715
716
717
718
719
720
# File 'ext/snow-data/snow-data.c', line 711

static VALUE sd_get_long(VALUE self, VALUE sd_offset)
{
  typedef long conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  value = *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset);
  return SD_LONG_TO_NUM(value);
}

#get_long_long(offset) ⇒ Integer

Reads a long long from the offset into the memory block and returns it. The offset is not bounds-checked and it is possible to read outside of bounds, which is considered undefined behavior and may crash or do other horrible things.

Returns:

  • (Integer)


750
751
752
753
754
755
756
757
758
759
# File 'ext/snow-data/snow-data.c', line 750

static VALUE sd_get_long_long(VALUE self, VALUE sd_offset)
{
  typedef long long conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  value = *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset);
  return SD_LONG_LONG_TO_NUM(value);
}

#get_ptrdiff_t(offset) ⇒ Object

Reads a ptrdiff_t from the offset into the memory block and returns it. The offset is not bounds-checked and it is possible to read outside of bounds, which is considered undefined behavior and may crash or do other horrible things.



594
595
596
597
598
599
600
601
602
603
# File 'ext/snow-data/snow-data.c', line 594

static VALUE sd_get_ptrdiff_t(VALUE self, VALUE sd_offset)
{
  typedef ptrdiff_t conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  value = *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset);
  return SD_PTRDIFF_T_TO_NUM(value);
}

#get_short(offset) ⇒ Integer

Reads a short from the offset into the memory block and returns it. The offset is not bounds-checked and it is possible to read outside of bounds, which is considered undefined behavior and may crash or do other horrible things.

Returns:

  • (Integer)


1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
# File 'ext/snow-data/snow-data.c', line 1023

static VALUE sd_get_short(VALUE self, VALUE sd_offset)
{
  typedef short conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  value = *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset);
  return SD_SHORT_TO_NUM(value);
}

#get_signed_char(offset) ⇒ Object

Reads a signed char from the offset into the memory block and returns it. The offset is not bounds-checked and it is possible to read outside of bounds, which is considered undefined behavior and may crash or do other horrible things.



1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
# File 'ext/snow-data/snow-data.c', line 1179

static VALUE sd_get_signed_char(VALUE self, VALUE sd_offset)
{
  typedef signed char conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  value = *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset);
  return SD_SIGNED_CHAR_TO_NUM(value);
}

#get_size_t(offset) ⇒ Object

Reads a size_t from the offset into the memory block and returns it. The offset is not bounds-checked and it is possible to read outside of bounds, which is considered undefined behavior and may crash or do other horrible things.



555
556
557
558
559
560
561
562
563
564
# File 'ext/snow-data/snow-data.c', line 555

static VALUE sd_get_size_t(VALUE self, VALUE sd_offset)
{
  typedef size_t conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  value = *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset);
  return SD_SIZE_T_TO_NUM(value);
}

#get_string(offset, length = nil) ⇒ String

Copies a string out of the block and returns it. The length argument is used to specify how the string is copied.

If length is nil, the string is extracted from offset up to the first null character. If length is -1, it extracts all characters from offset onward in the string and returns it. Otherwise, for any other length, it tries to copy length characters from the block before the end of the block.

This method does not work on zero-length blocks.

Returns:

  • (String)


1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
# File 'ext/snow-data/snow-data.c', line 1223

static VALUE sd_get_string(int argc, VALUE *argv, VALUE self)
{
  VALUE sd_offset;
  VALUE sd_length;
  size_t offset;
  size_t length       = ~(size_t)0;
  size_t self_length  = NUM2SIZET(rb_ivar_get(self, kSD_IVAR_BYTESIZE));
  const uint8_t *data = DATA_PTR(self);

  sd_check_null_block(self);

  rb_scan_args(argc, argv, "11", &sd_offset, &sd_length);

  offset = NUM2SIZET(sd_offset);

  if (offset >= self_length) {
    return rb_str_new("", 0);
  }

  if (RTEST(sd_length)) {
    length = NUM2SIZET(sd_length);

    if (length == ~(size_t)0
        || (offset + length) > self_length
        || (offset + length) < offset) {
      length = self_length - offset;
    }
  } else {
    for (length = offset; length < self_length && data[length]; ++length)
      ;
    length -= offset;
  }

  return rb_str_new((const char *)(data + offset), length);
}

#get_uint16(offset) ⇒ Object

Reads a uint16_t from the offset into the memory block and returns it. The offset is not bounds-checked and it is possible to read outside of bounds, which is considered undefined behavior and may crash or do other horrible things.



438
439
440
441
442
443
444
445
446
447
# File 'ext/snow-data/snow-data.c', line 438

static VALUE sd_get_uint16(VALUE self, VALUE sd_offset)
{
  typedef uint16_t conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  value = *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset);
  return SD_UINT16_TO_NUM(value);
}

#get_uint32(offset) ⇒ Object

Reads a uint32_t from the offset into the memory block and returns it. The offset is not bounds-checked and it is possible to read outside of bounds, which is considered undefined behavior and may crash or do other horrible things.



477
478
479
480
481
482
483
484
485
486
# File 'ext/snow-data/snow-data.c', line 477

static VALUE sd_get_uint32(VALUE self, VALUE sd_offset)
{
  typedef uint32_t conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  value = *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset);
  return SD_UINT32_TO_NUM(value);
}

#get_uint64(offset) ⇒ Object

Reads a uint64_t from the offset into the memory block and returns it. The offset is not bounds-checked and it is possible to read outside of bounds, which is considered undefined behavior and may crash or do other horrible things.



516
517
518
519
520
521
522
523
524
525
# File 'ext/snow-data/snow-data.c', line 516

static VALUE sd_get_uint64(VALUE self, VALUE sd_offset)
{
  typedef uint64_t conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  value = *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset);
  return SD_UINT64_TO_NUM(value);
}

#get_uint8(offset) ⇒ Object

Reads a uint8_t from the offset into the memory block and returns it. The offset is not bounds-checked and it is possible to read outside of bounds, which is considered undefined behavior and may crash or do other horrible things.



399
400
401
402
403
404
405
406
407
408
# File 'ext/snow-data/snow-data.c', line 399

static VALUE sd_get_uint8(VALUE self, VALUE sd_offset)
{
  typedef uint8_t conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  value = *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset);
  return SD_UINT8_TO_NUM(value);
}

#get_uintptr_t(offset) ⇒ Object

Reads a uintptr_t from the offset into the memory block and returns it. The offset is not bounds-checked and it is possible to read outside of bounds, which is considered undefined behavior and may crash or do other horrible things.



672
673
674
675
676
677
678
679
680
681
# File 'ext/snow-data/snow-data.c', line 672

static VALUE sd_get_uintptr_t(VALUE self, VALUE sd_offset)
{
  typedef uintptr_t conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  value = *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset);
  return SD_UINTPTR_T_TO_NUM(value);
}

#get_unsigned_char(offset) ⇒ Object

Reads a unsigned char from the offset into the memory block and returns it. The offset is not bounds-checked and it is possible to read outside of bounds, which is considered undefined behavior and may crash or do other horrible things.



1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
# File 'ext/snow-data/snow-data.c', line 1140

static VALUE sd_get_unsigned_char(VALUE self, VALUE sd_offset)
{
  typedef unsigned char conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  value = *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset);
  return SD_UNSIGNED_CHAR_TO_NUM(value);
}

#get_unsigned_int(offset) ⇒ Integer

Reads a unsigned int from the offset into the memory block and returns it. The offset is not bounds-checked and it is possible to read outside of bounds, which is considered undefined behavior and may crash or do other horrible things.

Returns:

  • (Integer)


984
985
986
987
988
989
990
991
992
993
# File 'ext/snow-data/snow-data.c', line 984

static VALUE sd_get_unsigned_int(VALUE self, VALUE sd_offset)
{
  typedef unsigned int conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  value = *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset);
  return SD_UNSIGNED_INT_TO_NUM(value);
}

#get_unsigned_long(offset) ⇒ Integer

Reads a unsigned long from the offset into the memory block and returns it. The offset is not bounds-checked and it is possible to read outside of bounds, which is considered undefined behavior and may crash or do other horrible things.

Returns:

  • (Integer)


789
790
791
792
793
794
795
796
797
798
# File 'ext/snow-data/snow-data.c', line 789

static VALUE sd_get_unsigned_long(VALUE self, VALUE sd_offset)
{
  typedef unsigned long conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  value = *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset);
  return SD_UNSIGNED_LONG_TO_NUM(value);
}

#get_unsigned_long_long(offset) ⇒ Integer

Reads a unsigned long long from the offset into the memory block and returns it. The offset is not bounds-checked and it is possible to read outside of bounds, which is considered undefined behavior and may crash or do other horrible things.

Returns:

  • (Integer)


828
829
830
831
832
833
834
835
836
837
# File 'ext/snow-data/snow-data.c', line 828

static VALUE sd_get_unsigned_long_long(VALUE self, VALUE sd_offset)
{
  typedef unsigned long long conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  value = *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset);
  return SD_UNSIGNED_LONG_LONG_TO_NUM(value);
}

#get_unsigned_short(offset) ⇒ Integer

Reads a unsigned short from the offset into the memory block and returns it. The offset is not bounds-checked and it is possible to read outside of bounds, which is considered undefined behavior and may crash or do other horrible things.

Returns:

  • (Integer)


1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
# File 'ext/snow-data/snow-data.c', line 1062

static VALUE sd_get_unsigned_short(VALUE self, VALUE sd_offset)
{
  typedef unsigned short conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  value = *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset);
  return SD_UNSIGNED_SHORT_TO_NUM(value);
}

#inspectObject

Returns a string showing the memory block’s classname, object ID, address, size, and alignment.



94
95
96
# File 'lib/snow-data/memory.rb', line 94

def inspect
  "<#{self.class}:0x#{__id__.to_s(16).rjust(14, ?0)} *0x#{self.address.to_s(16).rjust(14, ?0)}:#{self.bytesize}:#{self.alignment}>"
end

#null?Boolean

Returns whether the memory block is pointing to a null address.

Returns:

  • (Boolean)


68
69
70
# File 'lib/snow-data/memory.rb', line 68

def null?
  self.address == 0
end

#realloc!(size, alignment = nil) ⇒ self

Reallocates the memory backing this pointer with a new size and optionally a new alignment. If the new size is the same as the old size, the method returns early and nothing is reallocated.

If a new alignment is specified, the memory will be reallocated regardless of whether the new and old sizes are the same. If no alignment is specified, the memory’s previous alignment is used.

If the block for this memory was previously freed or the block is not owner by this object, a new block is allocated and the memory takes ownership of it. It is fine to realloc! on a previously freed block.

Raises a RangeError if either size is zero or alignment is not a power of two.

Returns:

  • (self)


1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
# File 'ext/snow-data/snow-data.c', line 1547

static VALUE sd_memory_realloc(int argc, VALUE *argv, VALUE self)
{
  struct RData *data;
  void *new_data;
  size_t size;
  size_t prev_size;
  size_t prev_align;
  size_t alignment;
  VALUE sd_size;
  VALUE sd_alignment;

  /*
    Don't check for null/zero length here, as it is safe to reuse a memory via
    realloc!. It's less safe for structs and arrays, but you just have to do the
    sane thing in most cases. Granted, I'm a hypocrite for saying you need to
    do the sane thing after writing this gem.
   */
  rb_check_frozen(self);

  rb_scan_args(argc, argv, "11", &sd_size, &sd_alignment);

  size       = NUM2SIZET(sd_size);
  prev_align =
  alignment  = NUM2SIZET(rb_ivar_get(self, kSD_IVAR_ALIGNMENT));
  prev_size  = NUM2SIZET(rb_ivar_get(self, kSD_IVAR_BYTESIZE));

  if (RTEST(sd_alignment)) {
    alignment = NUM2SIZET(sd_alignment);
  }

  if (prev_size == size && alignment == prev_align) {
    return self;
  } else if (!is_power_of_two(alignment)) {
    rb_raise(rb_eRangeError, "Alignment must be a power of two -- %zu is not a"
      " power of two", alignment);
  } else if (size < 1) {
    rb_raise(rb_eRangeError, "Size of block must be 1 or more -- zero-byte"
      " blocks are not permitted");
  }

  data      = RDATA(self);
  new_data  = com_malloc(size, alignment);

  if (data->data && prev_size > 0) {
    const size_t copy_sizes[2] = { prev_size, size };
    memcpy(new_data, data->data, copy_sizes[prev_size > size]);
  }

  if (data->dfree) {
    data->dfree(data->data);
  } else if (data->data) {
    rb_warning("realloc called on unowned pointer %p -- allocating new block"
      " and memcpying contents (size: %zd bytes), but original block will"
      " not be freed.", data->data, prev_size);
  }

  data->data  = new_data;
  data->dfree = com_free;

  rb_ivar_set(self, kSD_IVAR_BYTESIZE,  SIZET2NUM(size));
  rb_ivar_set(self, kSD_IVAR_ALIGNMENT, SIZET2NUM(alignment));
  return self;
}

#set_char(offset, value) ⇒ Object

Sets a char at the offset to the value. Returns the assigned value.



1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
# File 'ext/snow-data/snow-data.c', line 1118

static VALUE sd_set_char(VALUE self, VALUE sd_offset, VALUE sd_value)
{
  typedef char conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  rb_check_frozen(self);
  value = (conv_type_t)SD_NUM_TO_CHAR(sd_value);
  *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset) = value;
  return sd_value;
}

#set_double(offset, value) ⇒ Object

Sets a double at the offset to the value. Returns the assigned value.



923
924
925
926
927
928
929
930
931
932
933
934
# File 'ext/snow-data/snow-data.c', line 923

static VALUE sd_set_double(VALUE self, VALUE sd_offset, VALUE sd_value)
{
  typedef double conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  rb_check_frozen(self);
  value = (conv_type_t)SD_NUM_TO_DOUBLE(sd_value);
  *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset) = value;
  return sd_value;
}

#set_float(offset, value) ⇒ Object

Sets a float at the offset to the value. Returns the assigned value.



884
885
886
887
888
889
890
891
892
893
894
895
# File 'ext/snow-data/snow-data.c', line 884

static VALUE sd_set_float(VALUE self, VALUE sd_offset, VALUE sd_value)
{
  typedef float conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  rb_check_frozen(self);
  value = (conv_type_t)SD_NUM_TO_FLOAT(sd_value);
  *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset) = value;
  return sd_value;
}

#set_int(offset, value) ⇒ Object

Sets a int at the offset to the value. Returns the assigned value.



962
963
964
965
966
967
968
969
970
971
972
973
# File 'ext/snow-data/snow-data.c', line 962

static VALUE sd_set_int(VALUE self, VALUE sd_offset, VALUE sd_value)
{
  typedef int conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  rb_check_frozen(self);
  value = (conv_type_t)SD_NUM_TO_INT(sd_value);
  *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset) = value;
  return sd_value;
}

#set_int16(offset, value) ⇒ Object

Sets a int16_t at the offset to the value. Returns the assigned value.



299
300
301
302
303
304
305
306
307
308
309
310
# File 'ext/snow-data/snow-data.c', line 299

static VALUE sd_set_int16(VALUE self, VALUE sd_offset, VALUE sd_value)
{
  typedef int16_t conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  rb_check_frozen(self);
  value = (conv_type_t)SD_NUM_TO_INT16(sd_value);
  *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset) = value;
  return sd_value;
}

#set_int32(offset, value) ⇒ Object

Sets a int32_t at the offset to the value. Returns the assigned value.



338
339
340
341
342
343
344
345
346
347
348
349
# File 'ext/snow-data/snow-data.c', line 338

static VALUE sd_set_int32(VALUE self, VALUE sd_offset, VALUE sd_value)
{
  typedef int32_t conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  rb_check_frozen(self);
  value = (conv_type_t)SD_NUM_TO_INT32(sd_value);
  *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset) = value;
  return sd_value;
}

#set_int64(offset, value) ⇒ Object

Sets a int64_t at the offset to the value. Returns the assigned value.



377
378
379
380
381
382
383
384
385
386
387
388
# File 'ext/snow-data/snow-data.c', line 377

static VALUE sd_set_int64(VALUE self, VALUE sd_offset, VALUE sd_value)
{
  typedef int64_t conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  rb_check_frozen(self);
  value = (conv_type_t)SD_NUM_TO_INT64(sd_value);
  *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset) = value;
  return sd_value;
}

#set_int8(offset, value) ⇒ Object

Sets a int8_t at the offset to the value. Returns the assigned value.



260
261
262
263
264
265
266
267
268
269
270
271
# File 'ext/snow-data/snow-data.c', line 260

static VALUE sd_set_int8(VALUE self, VALUE sd_offset, VALUE sd_value)
{
  typedef int8_t conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  rb_check_frozen(self);
  value = (conv_type_t)SD_NUM_TO_INT8(sd_value);
  *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset) = value;
  return sd_value;
}

#set_intptr_t(offset, value) ⇒ Object

Sets a intptr_t at the offset to the value. Returns the assigned value.



650
651
652
653
654
655
656
657
658
659
660
661
# File 'ext/snow-data/snow-data.c', line 650

static VALUE sd_set_intptr_t(VALUE self, VALUE sd_offset, VALUE sd_value)
{
  typedef intptr_t conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  rb_check_frozen(self);
  value = (conv_type_t)SD_NUM_TO_INTPTR_T(sd_value);
  *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset) = value;
  return sd_value;
}

#set_long(offset, value) ⇒ Object

Sets a long at the offset to the value. Returns the assigned value.



728
729
730
731
732
733
734
735
736
737
738
739
# File 'ext/snow-data/snow-data.c', line 728

static VALUE sd_set_long(VALUE self, VALUE sd_offset, VALUE sd_value)
{
  typedef long conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  rb_check_frozen(self);
  value = (conv_type_t)SD_NUM_TO_LONG(sd_value);
  *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset) = value;
  return sd_value;
}

#set_long_long(offset, value) ⇒ Object

Sets a long long at the offset to the value. Returns the assigned value.



767
768
769
770
771
772
773
774
775
776
777
778
# File 'ext/snow-data/snow-data.c', line 767

static VALUE sd_set_long_long(VALUE self, VALUE sd_offset, VALUE sd_value)
{
  typedef long long conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  rb_check_frozen(self);
  value = (conv_type_t)SD_NUM_TO_LONG_LONG(sd_value);
  *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset) = value;
  return sd_value;
}

#set_ptrdiff_t(offset, value) ⇒ Object

Sets a ptrdiff_t at the offset to the value. Returns the assigned value.



611
612
613
614
615
616
617
618
619
620
621
622
# File 'ext/snow-data/snow-data.c', line 611

static VALUE sd_set_ptrdiff_t(VALUE self, VALUE sd_offset, VALUE sd_value)
{
  typedef ptrdiff_t conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  rb_check_frozen(self);
  value = (conv_type_t)SD_NUM_TO_PTRDIFF_T(sd_value);
  *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset) = value;
  return sd_value;
}

#set_short(offset, value) ⇒ Object

Sets a short at the offset to the value. Returns the assigned value.



1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
# File 'ext/snow-data/snow-data.c', line 1040

static VALUE sd_set_short(VALUE self, VALUE sd_offset, VALUE sd_value)
{
  typedef short conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  rb_check_frozen(self);
  value = (conv_type_t)SD_NUM_TO_SHORT(sd_value);
  *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset) = value;
  return sd_value;
}

#set_signed_char(offset, value) ⇒ Object

Sets a signed char at the offset to the value. Returns the assigned value.



1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
# File 'ext/snow-data/snow-data.c', line 1196

static VALUE sd_set_signed_char(VALUE self, VALUE sd_offset, VALUE sd_value)
{
  typedef signed char conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  rb_check_frozen(self);
  value = (conv_type_t)SD_NUM_TO_SIGNED_CHAR(sd_value);
  *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset) = value;
  return sd_value;
}

#set_size_t(offset, value) ⇒ Object

Sets a size_t at the offset to the value. Returns the assigned value.



572
573
574
575
576
577
578
579
580
581
582
583
# File 'ext/snow-data/snow-data.c', line 572

static VALUE sd_set_size_t(VALUE self, VALUE sd_offset, VALUE sd_value)
{
  typedef size_t conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  rb_check_frozen(self);
  value = (conv_type_t)SD_NUM_TO_SIZE_T(sd_value);
  *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset) = value;
  return sd_value;
}

#set_string(offset, value, null_terminated = false) ⇒ Object

Copies the string value into the block at the offset supplied.

If null_terminated is true, it will always write a null-terminating character if it fits. This means that you need at least string.bytesize + 1 bytes available from the offset onwards to fully story a string, otherwise the string’s contents will be truncated to fit the null-terminating character.

If null_terminated is false, no null character is written and only the string bytes are copied to the block. If the full string does not fit, it will be truncated.



1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
# File 'ext/snow-data/snow-data.c', line 1303

static VALUE sd_set_string(int argc, VALUE *argv, VALUE self)
{
  VALUE sd_offset, sd_value, sd_null_terminated;

  sd_check_null_block(self);
  rb_check_frozen(self);

  rb_scan_args(argc, argv, "21", &sd_offset, &sd_value, &sd_null_terminated);

  return sd_set_string_nullterm(self, sd_offset, sd_value, !!RTEST(sd_null_terminated));
}

#set_uint16(offset, value) ⇒ Object

Sets a uint16_t at the offset to the value. Returns the assigned value.



455
456
457
458
459
460
461
462
463
464
465
466
# File 'ext/snow-data/snow-data.c', line 455

static VALUE sd_set_uint16(VALUE self, VALUE sd_offset, VALUE sd_value)
{
  typedef uint16_t conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  rb_check_frozen(self);
  value = (conv_type_t)SD_NUM_TO_UINT16(sd_value);
  *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset) = value;
  return sd_value;
}

#set_uint32(offset, value) ⇒ Object

Sets a uint32_t at the offset to the value. Returns the assigned value.



494
495
496
497
498
499
500
501
502
503
504
505
# File 'ext/snow-data/snow-data.c', line 494

static VALUE sd_set_uint32(VALUE self, VALUE sd_offset, VALUE sd_value)
{
  typedef uint32_t conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  rb_check_frozen(self);
  value = (conv_type_t)SD_NUM_TO_UINT32(sd_value);
  *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset) = value;
  return sd_value;
}

#set_uint64(offset, value) ⇒ Object

Sets a uint64_t at the offset to the value. Returns the assigned value.



533
534
535
536
537
538
539
540
541
542
543
544
# File 'ext/snow-data/snow-data.c', line 533

static VALUE sd_set_uint64(VALUE self, VALUE sd_offset, VALUE sd_value)
{
  typedef uint64_t conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  rb_check_frozen(self);
  value = (conv_type_t)SD_NUM_TO_UINT64(sd_value);
  *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset) = value;
  return sd_value;
}

#set_uint8(offset, value) ⇒ Object

Sets a uint8_t at the offset to the value. Returns the assigned value.



416
417
418
419
420
421
422
423
424
425
426
427
# File 'ext/snow-data/snow-data.c', line 416

static VALUE sd_set_uint8(VALUE self, VALUE sd_offset, VALUE sd_value)
{
  typedef uint8_t conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  rb_check_frozen(self);
  value = (conv_type_t)SD_NUM_TO_UINT8(sd_value);
  *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset) = value;
  return sd_value;
}

#set_uintptr_t(offset, value) ⇒ Object

Sets a uintptr_t at the offset to the value. Returns the assigned value.



689
690
691
692
693
694
695
696
697
698
699
700
# File 'ext/snow-data/snow-data.c', line 689

static VALUE sd_set_uintptr_t(VALUE self, VALUE sd_offset, VALUE sd_value)
{
  typedef uintptr_t conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  rb_check_frozen(self);
  value = (conv_type_t)SD_NUM_TO_UINTPTR_T(sd_value);
  *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset) = value;
  return sd_value;
}

#set_unsigned_char(offset, value) ⇒ Object

Sets a unsigned char at the offset to the value. Returns the assigned value.



1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
# File 'ext/snow-data/snow-data.c', line 1157

static VALUE sd_set_unsigned_char(VALUE self, VALUE sd_offset, VALUE sd_value)
{
  typedef unsigned char conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  rb_check_frozen(self);
  value = (conv_type_t)SD_NUM_TO_UNSIGNED_CHAR(sd_value);
  *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset) = value;
  return sd_value;
}

#set_unsigned_int(offset, value) ⇒ Object

Sets a unsigned int at the offset to the value. Returns the assigned value.



1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
# File 'ext/snow-data/snow-data.c', line 1001

static VALUE sd_set_unsigned_int(VALUE self, VALUE sd_offset, VALUE sd_value)
{
  typedef unsigned int conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  rb_check_frozen(self);
  value = (conv_type_t)SD_NUM_TO_UNSIGNED_INT(sd_value);
  *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset) = value;
  return sd_value;
}

#set_unsigned_long(offset, value) ⇒ Object

Sets a unsigned long at the offset to the value. Returns the assigned value.



806
807
808
809
810
811
812
813
814
815
816
817
# File 'ext/snow-data/snow-data.c', line 806

static VALUE sd_set_unsigned_long(VALUE self, VALUE sd_offset, VALUE sd_value)
{
  typedef unsigned long conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  rb_check_frozen(self);
  value = (conv_type_t)SD_NUM_TO_UNSIGNED_LONG(sd_value);
  *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset) = value;
  return sd_value;
}

#set_unsigned_long_long(offset, value) ⇒ Object

Sets a unsigned long long at the offset to the value. Returns the assigned value.



845
846
847
848
849
850
851
852
853
854
855
856
# File 'ext/snow-data/snow-data.c', line 845

static VALUE sd_set_unsigned_long_long(VALUE self, VALUE sd_offset, VALUE sd_value)
{
  typedef unsigned long long conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  rb_check_frozen(self);
  value = (conv_type_t)SD_NUM_TO_UNSIGNED_LONG_LONG(sd_value);
  *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset) = value;
  return sd_value;
}

#set_unsigned_short(offset, value) ⇒ Object

Sets a unsigned short at the offset to the value. Returns the assigned value.



1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
# File 'ext/snow-data/snow-data.c', line 1079

static VALUE sd_set_unsigned_short(VALUE self, VALUE sd_offset, VALUE sd_value)
{
  typedef unsigned short conv_type_t;
  const size_t offset = NUM2SIZET(sd_offset);
  conv_type_t value;
  sd_check_block_bounds(self, offset, sizeof(conv_type_t));
  sd_check_null_block(self);
  rb_check_frozen(self);
  value = (conv_type_t)SD_NUM_TO_UNSIGNED_SHORT(sd_value);
  *(conv_type_t *)(((uint8_t *)RDATA(self)->data) + offset) = value;
  return sd_value;
}

#to_s(null_terminated = true) ⇒ String

Gets a string representation of the contents of this block. If null_terminated is true (or nil), the returned string will end before the first null character.

Returns:

  • (String)


1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
# File 'ext/snow-data/snow-data.c', line 1830

static VALUE sd_memory_to_s(int argc, VALUE *argv, VALUE self)
{
  VALUE null_terminated;
  size_t byte_size = NUM2SIZET(rb_ivar_get(self, kSD_IVAR_BYTESIZE));
  const char *data = DATA_PTR(self);

  sd_check_null_block(self);

  rb_scan_args(argc, argv, "01", &null_terminated);

  if (null_terminated == Qnil || RTEST(null_terminated)) {
    size_t string_length = 0;
    for (; string_length < byte_size && data[string_length]; ++string_length)
      ;
    byte_size = string_length;
  }

  return rb_str_new(data, byte_size);
}