Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Documentation on go generator #55

Merged
merged 2 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -160,4 +160,4 @@ cpp:
.PHONY: check
check: build
@go test $(shell go list ./cmd/... ./internal/... ./pkg/... | grep -v /internal/tlcodegen/test/gen/)
@go run honnef.co/go/tools/cmd/staticcheck@v0.4.7 ./... # update version together with github actions
@go run honnef.co/go/tools/cmd/staticcheck@v0.5.1 ./... # update version together with github actions
109 changes: 109 additions & 0 deletions docs/go.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Go генератор

tlgen детерминистский и генерирует при повторном запуске один и тот же код, так что
сгенерированный код можно добавлять в систему контроля версий.

По-умолчанию все комбинаторы генерируются в один internal пакет, плюс для каждого
namespace TL генерируется пакет с алиасами. Это сделано потому, что Go не поддерживает рекурсивные зависимости между пакетами, а TL поддерживает.

Для очень больших TL-файлов, содержащих тысячи комбинаторов, можно передать флаг --split-internal, тогда каждый тип помещается в собственный internal пакет.
Затем пакеты для типов, рекурсивно использующих друг друга, обьединяются.
Это сделано для того, чтобы при изменении одного комбинатора TL не приходилось перекомпилировать код большинства комбинаторов.

Также tlgen поддерживает опцию --typesWhiteList, если вам нужна только часть namespace или комбинаторов, то можно явно указать их имена.

# шаблоны

TL-шаблоны полностью инстанцируются (для каждой использованной комбинации аргументов).

Так что pair<int, int> pair<long, long> превращаются в PairIntInt и PairLongLong.


# простые типы

Отображаются в соответствующие типы Go

Nat (#) в uint32, int в int32, long в int64, Bool в bool.

String в string или []byte (В так называемые Bytes-версиях типов)

# векторы и туплы

Отображаются в массивы (если размер фиксирован), либо в слайсы.

Отображение в массивы важно, так как позволяет 3*3*[int] отобразить в тип [3][3]int, который можно разместить на стеке и передать как знаение без аллокаций.

Из-за этой оптимизации для следующих комбинаторов 3*[int] 4*[int] и n*[int] где n нефиксирован будет сгенрировано 3 разных Go типа.

# структуры

Отображаются в структуры Go.

# маски полей

Локальные маски полей хранятся в натуральном для TL виде uint32, если какое-то поле зависит от маски полей, то кроме поля будет сгенерированы методы
SetFieldX и IsSetFieldX который ставят и проверяют соответствующий бит маски.

Маски полей-аргументы шаблона не хранятся в объектах, а передаются снаружи в методы Read, Write.

Таким образом сгенерированные типы не содержат избыточных масок полей.

# обьединения

Генерируются в структуру Go, содержащую все варианты обьединения.
На первый взгляд это неоптимально с точки зрения памяти, но на самом деле позволяет идеально переиспользовать []byte и слайсы в
каждом варианте при повторном парсинге. Это часто перекрывает эффект от того, что сами структуры занимают немного больше.

В качестве селектора варианта используется приватное поле номер конструктора.

# перечисления

Похожи на обьединения, но содержат функции MakeXXX

# Bytes-версии и работы с минимумом аллокаций

tlgen генерирует эффективный код на go, который поддерживает стиль
программирования с reuse, позволяющий значительно уменьшить число аллокаций и стоимость GC.

Если комбинатор содержит TL string, то генерируется go string, так что при каждом чтении TL будет происходить аллокация каждой строки.
С помощь опции --generateByteVersions можно попросить tlgen сгенерировать в дополнение к основным также версии типов, где вместо строк используется []byte.

При чтении соответствуюший []byte будет переиспользоваться, и если длина достаточно, то новой аллокации не будет.

Таким образом, например, если сервер читает некий TL запрос, он может иметь sync.Pool структур, брать из пула структуру, десерилизоввать, отвечать на запрос, и затем класть структуру обратно в пул.
При работе сервера все строки и другие слайсы довольно быстро перестанут аллоцироваться, и таким образо деаллоцироваться.

Нужно только понимать, что всеми слайсами в TL объекте владеет сам TL-объект, и при необходимости долговременного хранения нужно либо скопировать себе данные, либо сослаться на них, но тогда не класть структуру обратно в пул для reuse.

# метаданные и фабрика

Генерируется также пакет meta и factory.

meta содержит метаданные для всех комбинаторов, доступные по magic или имени комбинатора.

Если импортировать пакет factory, то для каждого комбинатора можно будет в runtime получить интерфейс, который позволит читать записывать и перекодировать TL-обьекты в другой формат на лету.

# JSON-представление

Все сгенерированные типы кроме методов ReadTL и WriteTL содержат также методы ReadJSON и WriteJSON.

Также WriteJSON используется в реализации интерфейса String(), так что печать TL-обьекта на экран или в лог
выводит его в каноническом формате, который можно распарсить.

Больше информации про каноническое представление JSON есть в документе TLPrimer.pdf

Если передать флаг --generateSchemaDocumentation, то сгенерируется HTML с документацией и примерами JSON для каждого комбинатора.

# RPC

Если передать флаг --generateRPCCode, то для всех TL-функций будет сгенерирова код
для реализации сервера (Handler) и клиентов (Client) в каждом пакете соответствующем TL namespace.

Этот код требует пакет RPC, который находится в opensource как часть проекта statashouse.
В дальнейшем есть планы пепеместить код RPC в проект tlgen.

# random

Если передать флаг --generateRandomCode, то у каждого сгенерированного типа появится возможность
создавать детерминистски случайный экземпляр, это важно для тестов.

5 changes: 5 additions & 0 deletions docs/tldoc.ru.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# TL (Type Language)

Формат md не поддерживает цвета, так что лучше всего читать
исходный документ в формате pdf.

Этот же файл получен автоконвертером и без цветов на схемах гораздо менее понятен, чем оригинал.

## Общие сведения

TL это язык описания данных и формат хранения данных.
Expand Down
Loading