%a{annotate:rdoc:skip}
class IO
  # <!-- rdoc-file=io_buffer.c -->
  # IO::Buffer is a low-level efficient buffer for input/output. There are three
  # ways of using buffer:
  #
  # *   Create an empty buffer with ::new, fill it with data using #copy or
  #     #set_value, #set_string, get data with #get_string;
  # *   Create a buffer mapped to some string with ::for, then it could be used
  #     both for reading with #get_string or #get_value, and writing (writing will
  #     change the source string, too);
  # *   Create a buffer mapped to some file with ::map, then it could be used for
  #     reading and writing the underlying file.
  #
  #
  # Interaction with string and file memory is performed by efficient low-level C
  # mechanisms like `memcpy`.
  #
  # The class is meant to be an utility for implementing more high-level
  # mechanisms like Fiber::SchedulerInterface#io_read and
  # Fiber::SchedulerInterface#io_write.
  #
  # **Examples of usage:**
  #
  # Empty buffer:
  #
  #     buffer = IO::Buffer.new(8)  # create empty 8-byte buffer
  #     #  =>
  #     # #<IO::Buffer 0x0000555f5d1a5c50+8 INTERNAL>
  #     # ...
  #     buffer
  #     #  =>
  #     # <IO::Buffer 0x0000555f5d156ab0+8 INTERNAL>
  #     # 0x00000000  00 00 00 00 00 00 00 00
  #     buffer.set_string('test', 2) # put there bytes of the "test" string, starting from offset 2
  #     # => 4
  #     buffer.get_string  # get the result
  #     # => "\x00\x00test\x00\x00"
  #
  # Buffer from string:
  #
  #     string = 'data'
  #     buffer = IO::Buffer.for(str)
  #     #  =>
  #     # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
  #     # ...
  #     buffer
  #     #  =>
  #     # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
  #     # 0x00000000  64 61 74 61                                     data
  #
  #     buffer.get_string(2)  # read content starting from offset 2
  #     # => "ta"
  #     buffer.set_string('---', 1) # write content, starting from offset 1
  #     # => 3
  #     buffer
  #     #  =>
  #     # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
  #     # 0x00000000  64 2d 2d 2d                                     d---
  #     string  # original string changed, too
  #     # => "d---"
  #
  # Buffer from file:
  #
  #     File.write('test.txt', 'test data')
  #     # => 9
  #     buffer = IO::Buffer.map(File.open('test.txt'))
  #     #  =>
  #     # #<IO::Buffer 0x00007f3f0768c000+9 MAPPED IMMUTABLE>
  #     # ...
  #     buffer.get_string(5, 2) # read 2 bytes, starting from offset 5
  #     # => "da"
  #     buffer.set_string('---', 1) # attempt to write
  #     # in `set_string': Buffer is not writable! (IO::Buffer::AccessError)
  #
  #     # To create writable file-mapped buffer
  #     # Open file for read-write, pass size, offset, and flags=0
  #     buffer = IO::Buffer.map(File.open('test.txt', 'r+'), 9, 0, 0)
  #     buffer.set_string('---', 1)
  #     # => 3 -- bytes written
  #     File.read('test.txt')
  #     # => "t--- data"
  #
  # **The class is experimental and the interface is subject to change.**
  #
  class Buffer
    include Comparable

    # <!--
    #   rdoc-file=io_buffer.c
    #   - IO::Buffer.for(string) -> readonly io_buffer
    #   - IO::Buffer.for(string) {|io_buffer| ... read/write io_buffer ...}
    # -->
    # Creates a IO::Buffer from the given string's memory. Without a block a frozen
    # internal copy of the string is created efficiently and used as the buffer
    # source. When a block is provided, the buffer is associated directly with the
    # string's internal data and updating the buffer will update the string.
    #
    # Until #free is invoked on the buffer, either explicitly or via the garbage
    # collector, the source string will be locked and cannot be modified.
    #
    # If the string is frozen, it will create a read-only buffer which cannot be
    # modified.
    #
    #     string = 'test'
    #     buffer = IO::Buffer.for(string)
    #     buffer.external? #=> true
    #
    #     buffer.get_string(0, 1)
    #     # => "t"
    #     string
    #     # => "best"
    #
    #     buffer.resize(100)
    #     # in `resize': Cannot resize external buffer! (IO::Buffer::AccessError)
    #
    #     IO::Buffer.for(string) do |buffer|
    #       buffer.set_string("T")
    #       string
    #       # => "Test"
    #     end
    #
    def self.for: (String) -> Buffer

    # <!--
    #   rdoc-file=io_buffer.c
    #   - IO::Buffer.map(file, [size, [offset, [flags]]]) -> io_buffer
    # -->
    # Create an IO::Buffer for reading from `file` by memory-mapping the file.
    # `file_io` should be a `File` instance, opened for reading.
    #
    # Optional `size` and `offset` of mapping can be specified.
    #
    # By default, the buffer would be immutable (read only); to create a writable
    # mapping, you need to open a file in read-write mode, and explicitly pass
    # `flags` argument without IO::Buffer::IMMUTABLE.
    #
    #     File.write('test.txt', 'test')
    #
    #     buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::READONLY)
    #     # => #<IO::Buffer 0x00000001014a0000+4 MAPPED READONLY>
    #
    #     buffer.readonly?   # => true
    #
    #     buffer.get_string
    #     # => "test"
    #
    #     buffer.set_string('b', 0)
    #     # `set_string': Buffer is not writable! (IO::Buffer::AccessError)
    #
    #     # create read/write mapping: length 4 bytes, offset 0, flags 0
    #     buffer = IO::Buffer.map(File.open('test.txt', 'r+'), 4, 0)
    #     buffer.set_string('b', 0)
    #     # => 1
    #
    #     # Check it
    #     File.read('test.txt')
    #     # => "best"
    #
    # Note that some operating systems may not have cache coherency between mapped
    # buffers and file reads.
    #
    def self.map: (File file, ?Integer? size, ?Integer offset, ?Integer flags) -> Buffer

    public

    # <!--
    #   rdoc-file=io_buffer.c
    #   - <=>(other) -> true or false
    # -->
    # Buffers are compared by size and exact contents of the memory they are
    # referencing using `memcmp`.
    #
    def <=>: (Buffer) -> Integer

    # <!--
    #   rdoc-file=io_buffer.c
    #   - clear(value = 0, [offset, [length]]) -> self
    # -->
    # Fill buffer with `value`, starting with `offset` and going for `length` bytes.
    #
    #     buffer = IO::Buffer.for('test')
    #     # =>
    #     #   <IO::Buffer 0x00007fca40087c38+4 SLICE>
    #     #   0x00000000  74 65 73 74         test
    #
    #     buffer.clear
    #     # =>
    #     #   <IO::Buffer 0x00007fca40087c38+4 SLICE>
    #     #   0x00000000  00 00 00 00         ....
    #
    #     buf.clear(1) # fill with 1
    #     # =>
    #     #   <IO::Buffer 0x00007fca40087c38+4 SLICE>
    #     #   0x00000000  01 01 01 01         ....
    #
    #     buffer.clear(2, 1, 2) # fill with 2, starting from offset 1, for 2 bytes
    #     # =>
    #     #   <IO::Buffer 0x00007fca40087c38+4 SLICE>
    #     #   0x00000000  01 02 02 01         ....
    #
    #     buffer.clear(2, 1) # fill with 2, starting from offset 1
    #     # =>
    #     #   <IO::Buffer 0x00007fca40087c38+4 SLICE>
    #     #   0x00000000  01 02 02 02         ....
    #
    def clear: (?Integer value, ?Integer offset, ?Integer length) -> self

    # <!--
    #   rdoc-file=io_buffer.c
    #   - copy(source, [offset, [length, [source_offset]]]) -> size
    # -->
    # Efficiently copy data from a source IO::Buffer into the buffer, at `offset`
    # using `memcpy`. For copying String instances, see #set_string.
    #
    #     buffer = IO::Buffer.new(32)
    #     #  =>
    #     # #<IO::Buffer 0x0000555f5ca22520+32 INTERNAL>
    #     # 0x00000000  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    #     # 0x00000010  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................  *
    #
    #     buffer.copy(IO::Buffer.for("test"), 8)
    #     # => 4 -- size of data copied
    #     buffer
    #     #  =>
    #     # #<IO::Buffer 0x0000555f5cf8fe40+32 INTERNAL>
    #     # 0x00000000  00 00 00 00 00 00 00 00 74 65 73 74 00 00 00 00 ........test....
    #     # 0x00000010  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ *
    #
    # #copy can be used to put data into strings associated with buffer:
    #
    #     string= "data:    "
    #     # => "data:    "
    #     buffer = IO::Buffer.for(str)
    #     buffer.copy(IO::Buffer.for("test"), 5)
    #     # => 4
    #     string
    #     # => "data:test"
    #
    # Attempt to copy into a read-only buffer will fail:
    #
    #     File.write('test.txt', 'test')
    #     buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::READONLY)
    #     buffer.copy(IO::Buffer.for("test"), 8)
    #     # in `copy': Buffer is not writable! (IO::Buffer::AccessError)
    #
    # See ::map for details of creation of mutable file mappings, this will work:
    #
    #     buffer = IO::Buffer.map(File.open('test.txt', 'r+'))
    #     buffer.copy("boom", 0)
    #     # => 4
    #     File.read('test.txt')
    #     # => "boom"
    #
    # Attempt to copy the data which will need place outside of buffer's bounds will
    # fail:
    #
    #     buffer = IO::Buffer.new(2)
    #     buffer.copy('test', 0)
    #     # in `copy': Specified offset+length exceeds source size! (ArgumentError)
    #
    def copy: (Buffer source, ?Integer offset, ?Integer length, ?Integer source_offset) -> Integer

    # <!--
    #   rdoc-file=io_buffer.c
    #   - external? -> true or false
    # -->
    # If the buffer is _external_, meaning it references from memory which is not
    #     allocated or mapped by the buffer itself.
    #
    #     A buffer created using ::for has an external reference to the string's
    #     memory.
    #
    # External buffer can't be resized.
    #
    def empty?: () -> bool

    # <!--
    #   rdoc-file=io_buffer.c
    #   - external?()
    # -->
    #
    def external?: () -> bool

    # <!--
    #   rdoc-file=io_buffer.c
    #   - free -> self
    # -->
    # If the buffer references memory, release it back to the operating system.
    # *   for a *mapped* buffer (e.g. from file): unmap.
    # *   for a buffer created from scratch: free memory.
    # *   for a buffer created from string: undo the association.
    #
    #
    # After the buffer is freed, no further operations can't be performed on it.
    #
    #     buffer = IO::Buffer.for('test')
    #     buffer.free
    #     # => #<IO::Buffer 0x0000000000000000+0 NULL>
    #
    #     buffer.get_value(:U8, 0)
    #     # in `get_value': The buffer is not allocated! (IO::Buffer::AllocationError)
    #
    #     buffer.get_string
    #     # in `get_string': The buffer is not allocated! (IO::Buffer::AllocationError)
    #
    #     buffer.null?
    #     # => true
    #
    # You can resize a freed buffer to re-allocate it.
    #
    def free: () -> self

    # <!--
    #   rdoc-file=io_buffer.c
    #   - get_string([offset, [length, [encoding]]]) -> string
    # -->
    # Read a chunk or all of the buffer into a string, in the specified `encoding`.
    # If no encoding is provided `Encoding::BINARY` is used.
    #
    #     buffer = IO::Buffer.for('test')
    #     buffer.get_string
    #     # => "test"
    #     buffer.get_string(2)
    #     # => "st"
    #     buffer.get_string(2, 1)
    #     # => "s"
    #
    def get_string: (?Integer offset, ?Integer length, ?Encoding encoding) -> String

    # <!--
    #   rdoc-file=io_buffer.c
    #   - get_value(type, offset) -> numeric
    # -->
    # Read from buffer a value of `type` at `offset`. `type` should be one of
    # symbols:
    #
    # *   `:U8`: unsigned integer, 1 byte
    # *   `:S8`: signed integer, 1 byte
    # *   `:u16`: unsigned integer, 2 bytes, little-endian
    # *   `:U16`: unsigned integer, 2 bytes, big-endian
    # *   `:s16`: signed integer, 2 bytes, little-endian
    # *   `:S16`: signed integer, 2 bytes, big-endian
    # *   `:u32`: unsigned integer, 4 bytes, little-endian
    # *   `:U32`: unsigned integer, 4 bytes, big-endian
    # *   `:s32`: signed integer, 4 bytes, little-endian
    # *   `:S32`: signed integer, 4 bytes, big-endian
    # *   `:u64`: unsigned integer, 8 bytes, little-endian
    # *   `:U64`: unsigned integer, 8 bytes, big-endian
    # *   `:s64`: signed integer, 8 bytes, little-endian
    # *   `:S64`: signed integer, 8 bytes, big-endian
    # *   `:f32`: float, 4 bytes, little-endian
    # *   `:F32`: float, 4 bytes, big-endian
    # *   `:f64`: double, 8 bytes, little-endian
    # *   `:F64`: double, 8 bytes, big-endian
    #
    #
    # Example:
    #
    #     string = [1.5].pack('f')
    #     # => "\x00\x00\xC0?"
    #     IO::Buffer.for(string).get_value(:f32, 0)
    #     # => 1.5
    #
    def get_value: (int_get_type, Integer offset) -> Integer
                 | (float_get_type, Integer offset) -> Float

    type int_get_type = :U8 | :S8
                      | :u16 | :U16 | :s16 | :S16
                      | :u32 | :U32 | :s32 | :S32
                      | :u64 | :U64 | :s64 | :S64

    type float_get_type = :f32 | :F32 | :f64 | :F64

    # <!--
    #   rdoc-file=io_buffer.c
    #   - hexdump()
    # -->
    #
    def hexdump: () -> String

    # <!--
    #   rdoc-file=io_buffer.c
    #   - inspect()
    # -->
    #
    def inspect: () -> String

    # <!--
    #   rdoc-file=io_buffer.c
    #   - internal? -> true or false
    # -->
    # If the buffer is *internal*, meaning it references memory allocated by the
    # buffer itself.
    #
    # An internal buffer is not associated with any external memory (e.g. string) or
    # file mapping.
    #
    # Internal buffers are created using ::new and is the default when the requested
    # size is less than the IO::Buffer::PAGE_SIZE and it was not requested to be
    # mapped on creation.
    #
    # Internal buffers can be resized, and such an operation will typically
    # invalidate all slices, but not always.
    #
    def internal?: () -> bool

    # <!--
    #   rdoc-file=io_buffer.c
    #   - locked { ... }
    # -->
    # Allows to process a buffer in exclusive way, for concurrency-safety. While the
    # block is performed, the buffer is considered locked, and no other code can
    # enter the lock. Also, locked buffer can't be changed with #resize or #free.
    #
    #     buffer = IO::Buffer.new(4)
    #     buffer.locked? #=> false
    #
    #     Fiber.schedule do
    #       buffer.locked do
    #         buffer.write(io) # theoretical system call interface
    #       end
    #     end
    #
    #     Fiber.schedule do
    #       # in `locked': Buffer already locked! (IO::Buffer::LockedError)
    #       buffer.locked do
    #         buffer.set_string(...)
    #       end
    #     end
    #
    # The following operations acquire a lock: #resize, #free.
    #
    # Locking is not thread safe. It is designed as a safety net around non-blocking
    # system calls. You can only share a buffer between threads with appropriate
    # synchronisation techniques.
    #
    def locked: [A] () { (IO::Buffer) -> A } -> A

    # <!--
    #   rdoc-file=io_buffer.c
    #   - locked? -> true or false
    # -->
    # If the buffer is *locked*, meaning it is inside #locked block execution.
    # Locked buffer can't be resized or freed, and another lock can't be acquired on
    # it.
    #
    # Locking is not thread safe, but is a semantic used to ensure buffers don't
    # move while being used by a system call.
    #
    #     buffer.locked do
    #       buffer.write(io) # theoretical system call interface
    #     end
    #
    def locked?: () -> bool

    # <!--
    #   rdoc-file=io_buffer.c
    #   - mapped? -> true or false
    # -->
    # If the buffer is *mapped*, meaning it references memory mapped by the buffer.
    #
    # Mapped buffers are either anonymous, if created by ::new with the
    # IO::Buffer::MAPPED flag or if the size was at least IO::Buffer::PAGE_SIZE, or
    # backed by a file if created with ::map.
    #
    # Mapped buffers can usually be resized, and such an operation will typically
    # invalidate all slices, but not always.
    #
    def mapped?: () -> bool

    # <!--
    #   rdoc-file=io_buffer.c
    #   - null? -> true or false
    # -->
    # If the buffer was freed with #free or was never allocated in the first place.
    #
    def null?: () -> bool

    # <!--
    #   rdoc-file=io_buffer.c
    #   - pread(p1, p2, p3)
    # -->
    #
    def pread: (untyped, untyped, untyped) -> untyped

    # <!--
    #   rdoc-file=io_buffer.c
    #   - pwrite(p1, p2, p3)
    # -->
    #
    def pwrite: (untyped, untyped, untyped) -> untyped

    # <!--
    #   rdoc-file=io_buffer.c
    #   - read(p1, p2)
    # -->
    #
    def read: (untyped, untyped) -> untyped

    # <!--
    #   rdoc-file=io_buffer.c
    #   - readonly?()
    # -->
    #
    def readonly?: () -> bool

    # <!--
    #   rdoc-file=io_buffer.c
    #   - resize(new_size) -> self
    # -->
    # Resizes a buffer to a `new_size` bytes, preserving its content. Depending on
    # the old and new size, the memory area associated with the buffer might be
    # either extended, or rellocated at different address with content being copied.
    #
    #     buffer = IO::Buffer.new(4)
    #     buffer.set_string("test", 0)
    #     buffer.resize(8) # resize to 8 bytes
    #     #  =>
    #     # #<IO::Buffer 0x0000555f5d1a1630+8 INTERNAL>
    #     # 0x00000000  74 65 73 74 00 00 00 00                         test....
    #
    # External buffer (created with ::for), and locked buffer can not be resized.
    #
    def resize: (Integer) -> self

    # <!--
    #   rdoc-file=io_buffer.c
    #   - set_string(*args)
    # -->
    #
    def set_string: (*untyped) -> untyped

    # <!--
    #   rdoc-file=io_buffer.c
    #   - set_value(type, offset, value) -> offset
    # -->
    # Write to a buffer a `value` of `type` at `offset`. `type` should be one of
    # symbols described in #get_value.
    #
    #     buffer = IO::Buffer.new(8)
    #     #  =>
    #     # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
    #     # 0x00000000  00 00 00 00 00 00 00 00
    #     buffer.set_value(:U8, 1, 111)
    #     # => 1
    #     buffer
    #     #  =>
    #     # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
    #     # 0x00000000  00 6f 00 00 00 00 00 00                         .o......
    #
    # Note that if the `type` is integer and `value` is Float, the implicit
    # truncation is performed:
    #
    #     buffer = IO::Buffer.new(8)
    #     buffer.set_value(:U32, 0, 2.5)
    #     buffer
    #     #   =>
    #     #  #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
    #     #  0x00000000  00 00 00 02 00 00 00 00
    #     #                       ^^ the same as if we'd pass just integer 2
    #
    def set_value: (int_get_type | float_get_type, Integer offset, Float | Integer value) -> Integer

    # <!--
    #   rdoc-file=io_buffer.c
    #   - size -> integer
    # -->
    # Returns the size of the buffer that was explicitly set (on creation with ::new
    # or on #resize), or deduced on buffer's creation from string or file.
    #
    def size: () -> Integer

    # <!--
    #   rdoc-file=io_buffer.c
    #   - slice(offset, length) -> io_buffer
    # -->
    # Produce another IO::Buffer which is a slice (or view into) the current one
    # starting at `offset` bytes and going for `length` bytes.
    #
    # The slicing happens without copying of memory, and the slice keeps being
    # associated with the original buffer's source (string, or file), if any.
    #
    # Raises RuntimeError if the <tt>offset+length<tt> is out of the current
    # buffer's bounds.
    #
    #     string = 'test'
    #     buffer = IO::Buffer.for(string)
    #
    #     slice = buffer.slice(1, 2)
    #     # =>
    #     #  #<IO::Buffer 0x00007fc3d34ebc49+2 SLICE>
    #     #  0x00000000  65 73                                           es
    #
    #     # Put "o" into 0s position of the slice
    #     slice.set_string('o', 0)
    #     slice
    #     # =>
    #     #  #<IO::Buffer 0x00007fc3d34ebc49+2 SLICE>
    #     #  0x00000000  6f 73                                           os
    #
    #     # it is also visible at position 1 of the original buffer
    #     buffer
    #     # =>
    #     #  #<IO::Buffer 0x00007fc3d31e2d80+4 SLICE>
    #     #  0x00000000  74 6f 73 74                                     tost
    #
    #     # ...and original string
    #     string
    #     # => tost
    #
    def slice: (Integer offset, Integer length) -> Buffer

    # <!--
    #   rdoc-file=io_buffer.c
    #   - to_s -> string
    # -->
    # Short representation of the buffer. It includes the address, size and symbolic
    # flags. This format is subject to change.
    #
    #     puts IO::Buffer.new(4) # uses to_s internally
    #     # #<IO::Buffer 0x000055769f41b1a0+4 INTERNAL>
    #
    def to_s: () -> String

    # <!--
    #   rdoc-file=io_buffer.c
    #   - transfer -> new_io_buffer
    # -->
    # Transfers ownership to a new buffer, deallocating the current one.
    #
    #     buffer = IO::Buffer.new('test')
    #     other = buffer.transfer
    #     other
    #     #  =>
    #     # #<IO::Buffer 0x00007f136a15f7b0+4 SLICE>
    #     # 0x00000000  74 65 73 74                                     test
    #     buffer
    #     #  =>
    #     # #<IO::Buffer 0x0000000000000000+0 NULL>
    #     buffer.null?
    #     # => true
    #
    def transfer: () -> Buffer

    # <!--
    #   rdoc-file=io_buffer.c
    #   - valid? -> true or false
    # -->
    # Returns whether the buffer data is accessible.
    #
    # A buffer becomes invalid if it is a slice of another buffer which has been
    # freed.
    #
    def valid?: () -> bool

    # <!--
    #   rdoc-file=io_buffer.c
    #   - write(p1, p2)
    # -->
    #
    def write: (untyped, untyped) -> untyped

    private

    # <!--
    #   rdoc-file=io_buffer.c
    #   - IO::Buffer.new([size = DEFAULT_SIZE, [flags = 0]]) -> io_buffer
    # -->
    # Create a new zero-filled IO::Buffer of `size` bytes. By default, the buffer
    # will be *internal*: directly allocated chunk of the memory. But if the
    # requested `size` is more than OS-specific IO::Bufer::PAGE_SIZE, the buffer
    # would be allocated using the virtual memory mechanism (anonymous `mmap` on
    # Unix, `VirtualAlloc` on Windows). The behavior can be forced by passing
    # IO::Buffer::MAPPED as a second parameter.
    #
    # Examples
    #
    #     buffer = IO::Buffer.new(4)
    #     # =>
    #     #  #<IO::Buffer 0x000055b34497ea10+4 INTERNAL>
    #     #  0x00000000  00 00 00 00                                     ....
    #
    #     buffer.get_string(0, 1) # => "\x00"
    #
    #     buffer.set_string("test")
    #     buffer
    #     #  =>
    #     # #<IO::Buffer 0x000055b34497ea10+4 INTERNAL>
    #     # 0x00000000  74 65 73 74                                     test
    #
    def initialize: (?Integer size, ?Integer flags) -> void

    BIG_ENDIAN: Integer

    DEFAULT_SIZE: Integer

    EXTERNAL: Integer

    HOST_ENDIAN: Integer

    INTERNAL: Integer

    LITTLE_ENDIAN: Integer

    LOCKED: Integer

    MAPPED: Integer

    NETWORK_ENDIAN: Integer

    PAGE_SIZE: Integer

    PRIVATE: Integer

    READONLY: Integer

    class LockedError < RuntimeError
    end

    class AllocationError < RuntimeError
    end

    class AccessError < RuntimeError
    end

    class InvalidatedError < RuntimeError
    end

    class MaskError < ArgumentError
    end
  end
end
