---
title: "The structure of RITE - mruby-go"
date: 2023-07-12T00:00:00+08:00
publishDate: 2023-07-12T00:00:00+08:00
lastmod: 2023-09-03T17:21:26+08:00
tags: ["mruby","mruby-go","Ruby","Golang","Notes"]
series: "mruby-go"
toc: true
aiTranslated: true
permalink: "https://blog.aotoki.me/en/posts/2023/07/12/mruby-go-the-rite-structure/"
language: "en"
---


[mruby](https://mruby.org) compiles source code using a compiler (usually `mrbc`), resulting in a binary file in the `mrb` format. This file format is called RITE, and in order to execute compiled mruby code, it needs to be parsed and read.

<!--more-->

## Structure

The structure of RITE consists of two main parts: the [rite_binary_header](https://github.com/mruby/mruby/blob/3.2.0/include/mruby/dump.h#L81-L88) and several sections. The `rite_binary_header` records information about the format of the binary file, mruby version, compiler information, and more.

There are four basic types of sections: `irep`, `debug`, `lv` (Local Variable), and `footer` (meaningless). If the `debug` section is not specifically included, there will only be the other three types.

Each section has its own section header for identification. Apart from `irep`, which also includes additional version information (`rite_version`), other sections consist of an identity (`ident`) represented by `char[4]` and the size of the section represented by `uint32`.

```c
// example irep section
struct rite_section_irep_header {
  uint8_t section_ident[4];
  uint8_t section_size[4];
  uint8_t rite_version[4];
};
```

With this understanding, we can read this information using Golang.

## BinaryHeader

Reading the `rite_binary_header` is not difficult. We simply need to define a structure called `BinaryHeader` and use Sized Bytes (a fixed-size `byte` array) to correctly populate the values using the [Read](https://pkg.go.dev/encoding/binary#Read) method from the `binary` package.

```go
type BinaryHeader struct {
    Identifier [4]byte
	Version    struct {
		Major [4]byte
		Minor [4]byte
	}
	Size       uint32
	Compiler   struct {
		Name    [4]byte
		Version uint32
	}
}

func ReadHeader(r io.Reader) (*BinaryHeader, error) {
	header := &BinaryHeader{}
	err := binary.Read(r, binary.BigEndian, header)
	if err != nil {
		return nil, err
	}
	return header, nil
}
```

## Sections

Reading the sections is more complex and will be explained in subsequent articles. To differentiate a section, we can introduce a `SectionHeader` structure to extract the common section information.

```go
type SectionHeader struct {
	Identity [4]byte
	Size     uint32
}

func ReadSection(r io.Reader, remain uint32) (*Section, error) {
	header := &SectionHeader{}
	err := binary.Read(r, binary.BigEndian, header)
	if err != nil {
		return nil, err
	}

	isOverSize := header.Size > remain
	if isOverSize {
		return nil, errors.New("section size is larger than binary")
	}

	// ...

    sectionHeaderSize = uint32(unsafe.SizeOf(SectionHeader{}))
    noopBuffer := make([]byte, header.Size - sectionHeaderSize)
    _, err := r.Read(noopBuffer)
    if err != nil {
      return nil, err
    }

    return section, nil
}
```

In the operation of the virtual machine, both `BinaryHeader` and `SectionHeader` are nonessential information. Therefore, in actual implementation, these data will be discarded.

In the design of RITE, the size information contained in the header includes the size of the header itself. Therefore, when calculating, the size of the header needs to be subtracted. In the above example, we use `header.Size - sectionHeaderSize` to determine the size of the `noopBuffer` array that will be created, ensuring that the `io.Reader` can correctly stop at the beginning of the next section when reading.

> In the C language, arrays can be processed at arbitrary positions using the pointer feature. However, in Golang, after being encapsulated as an `io.Reader`, we can only read sequentially, so the position where the reading cursor stops becomes crucial.

---

[mruby-go](https://github.com/elct9620/mruby-go) is a project that aims to implement mruby entirely in Golang. It is expected to enable Golang to run Ruby, allowing for more flexibility in development, such as implementing DSLs or hooks.

