# frozen_string_literal: true # https://cstack.github.io/db_tutorial/parts/part3.html module Sqliterb class Table # In-Memory page class Page PAGE_SIZE = 4096 def initialize bytes = Array.new(PAGE_SIZE) { 0 }.pack('C*') @memory = StringIO.new(bytes).binmode end def read(pos, serializer) @memory.pos = pos serializer.read(@memory) end def write(pos, serializer) @memory.pos = pos serializer.write(@memory) end end class Serializer < BinData::Record SIZE = 32 + 32 + 255 endian :little uint32 :id string :username, length: 32, trim_padding: true string :email, length: 255, trim_padding: true end MAX_PAGES = 100 ROW_SIZE = Serializer::SIZE ROWS_PER_PAGE = Page::PAGE_SIZE / ROW_SIZE TABLE_MAX_ROWS = ROWS_PER_PAGE * MAX_PAGES def initialize @pages = Array.new(MAX_PAGES) @rows_num = 0 end def read(_row_data) @rows_num.times do |row_num| page, offset = find_row_slot(row_num) record = Serializer.new page.read(offset, record) print_row(record) end end def write(row_data) raise 'EXECUTE_TABLE_FULL' if @rows_num >= TABLE_MAX_ROWS page, offset = find_row_slot(@rows_num) page.write(offset, Serializer.new(normalize_args(row_data))) @rows_num += 1 end private def print_row(data) puts "(#{data.snapshot.values.join(', ')})" end # TODO: брать атрибуты из сериалайзера def normalize_args(data) { id: data[0].to_i, username: data[1], email: data[2] } end def find_row_slot(row_num) page_num = row_num / ROWS_PER_PAGE page = if @pages[page_num].nil? @pages[page_num] = Page.new else @pages[page_num] end row_offset = row_num % ROWS_PER_PAGE byte_offset = row_offset * ROW_SIZE [page, byte_offset] end end end