rabbit-slide-hasumikin-RubyKaigi2023 2022.05.13.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rabbit +1 -0
- data/BuildYourOwnSQLite3.rab +893 -0
- data/README.rd +24 -0
- data/Rakefile +17 -0
- data/config.yaml +24 -0
- data/demo.md +14 -0
- data/images/GPK60-46W.jpg +0 -0
- data/images/GPK60-46W_top.jpg +0 -0
- data/images/QR_github-com-picoruby.png +0 -0
- data/images/R2P2.jpg +0 -0
- data/images/assets/2023_graphic_kit/background.png +0 -0
- data/images/assets/2023_graphic_kit/ogp.png +0 -0
- data/images/assets/background-slide.png +0 -0
- data/images/assets/background-title.png +0 -0
- data/images/assets/bullet-point-24.png +0 -0
- data/images/assets/bullet-point-36.png +0 -0
- data/images/assets/bullet-point-48.png +0 -0
- data/images/assets/bullet-point.png +0 -0
- data/images/assets/main-visual-large.png +0 -0
- data/images/assets/main-visual-medium.png +0 -0
- data/images/assets/main-visual-small.png +0 -0
- data/images/hasumi.jpg +0 -0
- data/images/initial-V.png +0 -0
- data/images/kosaki.png +0 -0
- data/images/out-of-memory_1.png +0 -0
- data/images/out-of-memory_2.png +0 -0
- data/images/out-of-memory_fixed.png +0 -0
- data/images/picoruby.png +0 -0
- data/images/rpi_pico.jpg +0 -0
- data/images/transparent.png +0 -0
- data/images/vfs1.gif +0 -0
- data/memo_/345/220/214/346/231/202/351/200/232/350/250/263/347/224/250/350/252/236/351/233/206.md +115 -0
- data/pdf/RubyKaigi2023-BuildYourOwnSQLite3.pdf +0 -0
- data/theme.rb +191 -0
- metadata +91 -0
@@ -0,0 +1,893 @@
|
|
1
|
+
= Build Your
|
2
|
+
|
3
|
+
: subtitle
|
4
|
+
Own SQLite3
|
5
|
+
: author
|
6
|
+
hasumikin
|
7
|
+
: content-source
|
8
|
+
RubyKaigi 2023
|
9
|
+
: date
|
10
|
+
Mar. 13, 2023
|
11
|
+
: allotted-time
|
12
|
+
30m
|
13
|
+
: theme
|
14
|
+
theme
|
15
|
+
|
16
|
+
= How do you pronounce "SQLite"?
|
17
|
+
|
18
|
+
* /eskjuːel-aɪt/ エスキューエルアイト
|
19
|
+
* /eskjuːelaɪt/ エスキューエライト
|
20
|
+
* /eskjuːlaɪt/ エスキューライト
|
21
|
+
* /siːkuəl-aɪt/ スィークエルアイト
|
22
|
+
|
23
|
+
(('tag:center'))
|
24
|
+
\n
|
25
|
+
Just curious. Answer on\n
|
26
|
+
https://twitter.com/hasumikin
|
27
|
+
|
28
|
+
= SQLite3 - Features
|
29
|
+
|
30
|
+
* Public domain
|
31
|
+
* No limitation for commercial use
|
32
|
+
* Single-file database. No client-server style
|
33
|
+
* Easy to backup
|
34
|
+
* High performance while low resource usage
|
35
|
+
|
36
|
+
(('tag:large'))(('tag:center'))
|
37
|
+
\n
|
38
|
+
Good for embedded system
|
39
|
+
|
40
|
+
= Embedded SQLite3 in
|
41
|
+
|
42
|
+
* Car navigation system
|
43
|
+
* iOS | Android applications
|
44
|
+
* Web applications such as Rails
|
45
|
+
* Master data such as postal code
|
46
|
+
* Browsers like Google Chrome
|
47
|
+
* "Local" DB for browser apps
|
48
|
+
* ((*One-chip microcontroller*))
|
49
|
+
|
50
|
+
= Embedded SQLite3 in
|
51
|
+
|
52
|
+
# image
|
53
|
+
# src = images/transparent.png
|
54
|
+
# align = right
|
55
|
+
# relative-height = 100
|
56
|
+
# relative_margin_top = 0
|
57
|
+
# relative_margin_left = 50
|
58
|
+
# draw0 = [rectangle, false, 0, 0, 0.8, 0.45, {color: red, line_width: 8}]
|
59
|
+
# draw1 = [text, with OS, 0.56, 0.15, {color: red, size: 80, font_family: 'Montserrat', weight: bold}]
|
60
|
+
# draw2 = [rectangle, false, 0, 0.48, 0.8, 0.35, {color: red, line_width: 8}]
|
61
|
+
# draw3 = [text, without OS, 0.49, 0.58, {color: red, size: 80, font_family: 'Montserrat', weight: bold}]
|
62
|
+
|
63
|
+
* Car navigation system
|
64
|
+
* iOS | Android applications
|
65
|
+
* Web applications such as Rails
|
66
|
+
* Master data such as postal code
|
67
|
+
* Browsers like Google Chrome
|
68
|
+
* "Local" DB for browser apps
|
69
|
+
* ((*One-chip microcontroller*))
|
70
|
+
|
71
|
+
= Target device and OS
|
72
|
+
|
73
|
+
* RP2040
|
74
|
+
* RAM: 264 KB
|
75
|
+
* CPU: 32 bit Cortex-M0+ (dual)
|
76
|
+
* Flash ROM: 2 MB (Raspberry Pi Pico)
|
77
|
+
* Bare metal (No OS)
|
78
|
+
* Instead, ((*PicoRuby*)) takes care of\neverything
|
79
|
+
|
80
|
+
# image
|
81
|
+
# src = images/rpi_pico.jpg
|
82
|
+
# align = right
|
83
|
+
# relative-height = 90
|
84
|
+
# relative_margin_left = 5
|
85
|
+
|
86
|
+
= self.inspect
|
87
|
+
|
88
|
+
* hasumikin | ハスミキン(Twitter, GitHub)
|
89
|
+
* Creator of PicoRuby and PRK Firmware
|
90
|
+
* Committer of mruby/c
|
91
|
+
* Maintainer of IRB and Reline
|
92
|
+
* First prize of Fukuoka Ruby Award\n(2020 and 2022✌️)
|
93
|
+
* A final nominee of Ruby Prize 2021
|
94
|
+
* Monstarlab Shimane Dev. branch
|
95
|
+
|
96
|
+
# image
|
97
|
+
# src = images/hasumi.jpg
|
98
|
+
# align = right
|
99
|
+
# relative-height = 60
|
100
|
+
# relative_margin_top = 10
|
101
|
+
# relative_margin_left = 20
|
102
|
+
|
103
|
+
= Technical advisor of Monstarlab
|
104
|
+
|
105
|
+
* From May 1st, 2023
|
106
|
+
* Kosaki-san
|
107
|
+
* CRuby committer
|
108
|
+
* Linux Kernel committer
|
109
|
+
* The man of "Malloc"
|
110
|
+
|
111
|
+
# image
|
112
|
+
# src = images/kosaki.png
|
113
|
+
# align = right
|
114
|
+
# relative-height = 100
|
115
|
+
# relative_margin_top = 0
|
116
|
+
# relative_margin_left = 20
|
117
|
+
|
118
|
+
= Demo
|
119
|
+
|
120
|
+
# image
|
121
|
+
# src = images/R2P2.jpg
|
122
|
+
# relative-height = 100
|
123
|
+
# draw0 = [text, SD card, 0.21, 0.48, {color: white, size: 40, font_family: 'Courier Prime', weight: bold}]
|
124
|
+
# draw1 = [text, RTC, 0.45, 0.60, {color: white, size: 40, font_family: 'Courier Prime', weight: bold}]
|
125
|
+
# draw2 = [text, RP2040, 0.58, 0.47, {color: white, size: 40, font_family: 'Courier Prime', weight: bold}]
|
126
|
+
|
127
|
+
= Demo (review or fallback)
|
128
|
+
|
129
|
+
* R2P2 (Unix-like shell of PicoRuby)
|
130
|
+
* PicoIRB
|
131
|
+
* require 'sqilte3'
|
132
|
+
* db = SQLite3::database.new 'test.db'
|
133
|
+
* Interoperable database file between the laptop and the microcontroller
|
134
|
+
|
135
|
+
= Building an SQLite3 shared library
|
136
|
+
|
137
|
+
#enscript bash
|
138
|
+
# Download and unzip sqlite3.c and sqlite3.h
|
139
|
+
# Then,
|
140
|
+
# Typical build on Unix systems
|
141
|
+
gcc -shared -fPIC sqlite3.c -o libsqlite3.so
|
142
|
+
|
143
|
+
# On Windows (Microsoft Visual C++)
|
144
|
+
cl sqlite3.c -link -dll -out:sqlite3.dll
|
145
|
+
|
146
|
+
(('tag:center'))
|
147
|
+
(('tag:x-large'))
|
148
|
+
Easy-peasy🍋
|
149
|
+
|
150
|
+
= Compile-time options
|
151
|
+
|
152
|
+
# enscript console
|
153
|
+
# Platform Configuration SQLITE_MAX_LENGTH SQLITE_ENABLE_SESSION SQLITE_OMIT_FOREIGN_KEY
|
154
|
+
_HAVE_SQLITE_CONFIG_H SQLITE_MAX_LIKE_PATTERN_LENGTH SQLITE_ENABLE_SNAPSHOT SQLITE_OMIT_GENERATED_COLUMNS
|
155
|
+
HAVE_FDATASYNC SQLITE_MAX_PAGE_COUNT SQLITE_ENABLE_SORTER_REFERENCES SQLITE_OMIT_GET_TABLE
|
156
|
+
HAVE_GMTIME_R SQLITE_MAX_SQL_LENGTH SQLITE_ENABLE_STMT_SCANSTATUS SQLITE_OMIT_HEX_INTEGER
|
157
|
+
HAVE_ISNAN SQLITE_MAX_VARIABLE_NUMBER SQLITE_ENABLE_STMTVTAB SQLITE_OMIT_INCRBLOB
|
158
|
+
HAVE_LOCALTIME_R SQLITE_RTREE_INT_ONLY SQLITE_OMIT_INTEGRITY_CHECK
|
159
|
+
HAVE_LOCALTIME_S # Options To Control Operating Characteristics SQLITE_ENABLE_SQLLOG SQLITE_OMIT_INTROSPECTION_PRAGMAS
|
160
|
+
HAVE_MALLOC_USABLE_SIZE SQLITE_4_BYTE_ALIGNED_MALLOC SQLITE_ENABLE_STAT2 SQLITE_OMIT_JSON
|
161
|
+
HAVE_STRCHRNUL SQLITE_CASE_SENSITIVE_LIKE SQLITE_ENABLE_STAT3 SQLITE_OMIT_LIKE_OPTIMIZATION
|
162
|
+
HAVE_UTIME SQLITE_DIRECT_OVERFLOW_READ SQLITE_ENABLE_STAT4 SQLITE_OMIT_LOAD_EXTENSION
|
163
|
+
SQLITE_BYTEORDER=(0|1234|4321) SQLITE_HAVE_ISNAN SQLITE_ENABLE_TREE_EXPLAIN SQLITE_OMIT_LOCALTIME
|
164
|
+
SQLITE_MAX_ALLOCATION_SIZE=N SQLITE_ENABLE_UPDATE_DELETE_LIMIT SQLITE_OMIT_LOOKASIDE
|
165
|
+
# Options To Set Default Parameter Values SQLITE_OS_OTHER=<0 or 1> SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION SQLITE_OMIT_MEMORYDB
|
166
|
+
SQLITE_DEFAULT_AUTOMATIC_INDEX=<0 or 1> SQLITE_SECURE_DELETE SQLITE_ENABLE_UNLOCK_NOTIFY SQLITE_OMIT_OR_OPTIMIZATION
|
167
|
+
See also: SQLITE_OMIT_AUTOMATIC_INDEX SQLITE_THREADSAFE=<0 or 1 or 2> SQLITE_INTROSPECTION_PRAGMAS SQLITE_OMIT_PAGER_PRAGMAS
|
168
|
+
SQLITE_DEFAULT_AUTOVACUUM=<0 or 1 or 2> SQLITE_CONFIG_SINGLETHREAD SQLITE_SOUNDEX SQLITE_OMIT_PRAGMA
|
169
|
+
SQLITE_DEFAULT_CACHE_SIZE=<N> SQLITE_CONFIG_MULTITHREAD SQLITE_USE_ALLOCA SQLITE_OMIT_PROGRESS_CALLBACK
|
170
|
+
SQLITE_DEFAULT_FILE_FORMAT=<1 or 4> SQLITE_CONFIG_SERIALIZED SQLITE_USE_FCNTL_TRACE SQLITE_OMIT_QUICKBALANCE
|
171
|
+
SQLITE_DEFAULT_FILE_PERMISSIONS=N SQLITE_TEMP_STORE=<0 through 3> SQLITE_HAVE_ZLIB SQLITE_OMIT_REINDEX
|
172
|
+
SQLITE_DEFAULT_FOREIGN_KEYS=<0 or 1> SQLITE_TRACE_SIZE_LIMIT=N YYTRACKMAXSTACKDEPTH SQLITE_OMIT_SCHEMA_PRAGMAS
|
173
|
+
SQLITE_DEFAULT_MMAP_SIZE=N SQLITE_TRUSTED_SCHEMA=<0 or 1> SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS
|
174
|
+
SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT=<bytes> SQLITE_USE_URI # Options To Disable Features Normally Turned On SQLITE_OMIT_SHARED_CACHE
|
175
|
+
SQLITE_DEFAULT_LOCKING_MODE=<1 or 0> SQLITE_DISABLE_LFS SQLITE_OMIT_SUBQUERY
|
176
|
+
SQLITE_DEFAULT_LOOKASIDE=SZ,N # Options To Enable Features Normally Turned Off SQLITE_DISABLE_DIRSYNC SQLITE_OMIT_TCL_VARIABLE
|
177
|
+
SQLITE_DEFAULT_MEMSTATUS=<1 or 0> SQLITE_ALLOW_URI_AUTHORITY SQLITE_DISABLE_FTS3_UNICODE SQLITE_OMIT_TEMPDB
|
178
|
+
SQLITE_DEFAULT_PCACHE_INITSZ=N SQLITE_ALLOW_COVERING_INDEX_SCAN=<0 or 1> SQLITE_DISABLE_FTS4_DEFERRED SQLITE_OMIT_TRACE
|
179
|
+
SQLITE_DEFAULT_PAGE_SIZE=<bytes> SQLITE_ENABLE_8_3_NAMES=<1 or 2> SQLITE_DISABLE_INTRINSIC SQLITE_OMIT_TRIGGER
|
180
|
+
SQLITE_DEFAULT_SYNCHRONOUS=<0-3> SQLITE_ENABLE_API_ARMOR SQLITE_OMIT_TRUNCATE_OPTIMIZATION
|
181
|
+
SQLITE_DEFAULT_WAL_SYNCHRONOUS=<0-3> SQLITE_ENABLE_ATOMIC_WRITE # Options To Omit Features SQLITE_OMIT_UTF16
|
182
|
+
SQLITE_DEFAULT_WAL_AUTOCHECKPOINT=<pages> SQLITE_ENABLE_BATCH_ATOMIC_WRITE SQLITE_OMIT_ALTERTABLE SQLITE_OMIT_VACUUM
|
183
|
+
SQLITE_DEFAULT_WORKER_THREADS=N SQLITE_ENABLE_BYTECODE_VTAB SQLITE_OMIT_ANALYZE SQLITE_OMIT_VIEW
|
184
|
+
SQLITE_DQS=N SQLITE_ENABLE_COLUMN_METADATA SQLITE_OMIT_ATTACH SQLITE_OMIT_VIRTUALTABLE
|
185
|
+
SQLITE_EXTRA_DURABLE SQLITE_ENABLE_DBPAGE_VTAB SQLITE_OMIT_AUTHORIZATION SQLITE_OMIT_WAL
|
186
|
+
SQLITE_FTS3_MAX_EXPR_DEPTH=N SQLITE_ENABLE_DBSTAT_VTAB SQLITE_OMIT_AUTOINCREMENT SQLITE_OMIT_WINDOWFUNC
|
187
|
+
SQLITE_LIKE_DOESNT_MATCH_BLOBS SQLITE_ENABLE_DESERIALIZE SQLITE_OMIT_AUTOINIT SQLITE_OMIT_WSD
|
188
|
+
SQLITE_MAX_MEMORY=N SQLITE_ENABLE_EXPLAIN_COMMENTS SQLITE_OMIT_AUTOMATIC_INDEX SQLITE_OMIT_XFER_OPT
|
189
|
+
SQLITE_MAX_MMAP_SIZE=N SQLITE_ENABLE_FTS3 SQLITE_OMIT_AUTORESET SQLITE_UNTESTABLE
|
190
|
+
SQLITE_MAX_SCHEMA_RETRY=N SQLITE_ENABLE_FTS3_PARENTHESIS SQLITE_OMIT_AUTOVACUUM SQLITE_ZERO_MALLOC
|
191
|
+
SQLITE_MAX_WORKER_THREADS=N SQLITE_ENABLE_FTS3_TOKENIZER SQLITE_OMIT_BETWEEN_OPTIMIZATION
|
192
|
+
SQLITE_MEMDB_DEFAULT_MAXSIZE=N SQLITE_ENABLE_FTS4 SQLITE_OMIT_BLOB_LITERAL # Analysis and Debugging Options
|
193
|
+
SQLITE_MINIMUM_FILE_DESCRIPTOR=N SQLITE_ENABLE_FTS5 SQLITE_OMIT_BTREECOUNT SQLITE_DEBUG
|
194
|
+
SQLITE_POWERSAFE_OVERWRITE=<0 or 1> SQLITE_ENABLE_GEOPOLY SQLITE_OMIT_BUILTIN_TEST SQLITE_MEMDEBUG
|
195
|
+
SQLITE_PRINTF_PRECISION_LIMIT=N SQLITE_ENABLE_ICU SQLITE_OMIT_CASE_SENSITIVE_LIKE_PRAGMA
|
196
|
+
SQLITE_QUERY_PLANNER_LIMIT=N SQLITE_ENABLE_IOTRACE SQLITE_OMIT_CAST # Windows-Specific Options
|
197
|
+
SQLITE_QUERY_PLANNER_LIMIT_INCR=N SQLITE_ENABLE_MATH_FUNCTIONS SQLITE_OMIT_CHECK SQLITE_WIN32_HEAP_CREATE
|
198
|
+
SQLITE_REVERSE_UNORDERED_SELECTS SQLITE_ENABLE_JSON1 SQLITE_OMIT_COMPILEOPTION_DIAGS SQLITE_WIN32_MALLOC_VALIDATE
|
199
|
+
SQLITE_SORTER_PMASZ=N SQLITE_ENABLE_LOCKING_STYLE SQLITE_OMIT_COMPLETE
|
200
|
+
SQLITE_STMTJRNL_SPILL=N SQLITE_ENABLE_MEMORY_MANAGEMENT SQLITE_OMIT_COMPOUND_SELECT # Compiler Linkage and Calling Convention Control
|
201
|
+
SQLITE_WIN32_MALLOC SQLITE_ENABLE_MEMSYS3 SQLITE_OMIT_CTE SQLITE_API
|
202
|
+
YYSTACKDEPTH=<max_depth> SQLITE_ENABLE_MEMSYS5 SQLITE_OMIT_DATETIME_FUNCS SQLITE_APICALL
|
203
|
+
SQLITE_ENABLE_NORMALIZE SQLITE_OMIT_DECLTYPE SQLITE_CALLBACK
|
204
|
+
# Options To Set Size Limits SQLITE_ENABLE_NULL_TRIM SQLITE_OMIT_DEPRECATED SQLITE_CDECL
|
205
|
+
SQLITE_MAX_ATTACHED SQLITE_ENABLE_OFFSET_SQL_FUNC SQLITE_OMIT_DESERIALIZE SQLITE_EXTERN
|
206
|
+
SQLITE_MAX_COLUMN SQLITE_ENABLE_PREUPDATE_HOOK SQLITE_OMIT_DISKIO SQLITE_STDCALL
|
207
|
+
SQLITE_MAX_COMPOUND_SELECT SQLITE_ENABLE_QPSG SQLITE_OMIT_EXPLAIN SQLITE_SYSAPI
|
208
|
+
SQLITE_MAX_EXPR_DEPTH SQLITE_ENABLE_RBU SQLITE_OMIT_FLAG_PRAGMAS SQLITE_TCLAPI
|
209
|
+
SQLITE_MAX_FUNCTION_ARG SQLITE_ENABLE_RTREE SQLITE_OMIT_FLOATING_POINT
|
210
|
+
|
211
|
+
(('tag:center'))
|
212
|
+
(('tag:xx-small:https://sqlite.org/compile.html'))
|
213
|
+
|
214
|
+
= Compile-time options
|
215
|
+
|
216
|
+
SQLITE_OS_OTHER=<0 or 1> (default: 0)
|
217
|
+
|
218
|
+
* If the OS is other than Unix, Windows or OS/2,
|
219
|
+
* You'll specify `SQLITE_OS_OTHER=1`,
|
220
|
+
* Then you must provide:
|
221
|
+
* int sqlite3_os_init(void);
|
222
|
+
* int sqlite3_os_end(void);
|
223
|
+
|
224
|
+
|
225
|
+
= int sqlite3_os_init(void);
|
226
|
+
|
227
|
+
# enscript c
|
228
|
+
sqlite3_mem_methods my_mem_methods = {...}
|
229
|
+
sqlite3_vfs my_vfs = {...}
|
230
|
+
/*
|
231
|
+
* Typical implementation
|
232
|
+
*/
|
233
|
+
int
|
234
|
+
sqlite3_os_init(void)
|
235
|
+
{
|
236
|
+
sqlite3_config(SQLITE_CONFIG_MALLOC, &my_mem_methods);
|
237
|
+
sqlite3_initialize();
|
238
|
+
return sqlite3_vfs_register(&my_vfs, 1);
|
239
|
+
}
|
240
|
+
|
241
|
+
= When a Ruby app opens a DB
|
242
|
+
|
243
|
+
# image
|
244
|
+
# src = images/transparent.png
|
245
|
+
# relative-height = 94
|
246
|
+
# relative_margin_top = 2
|
247
|
+
# draw97 = [rectangle, true, 0.08, 0.0, 0.4, 0.16, {color: red}]
|
248
|
+
# draw98 = [text, SQLite3::Database.open, 0.1, 0.04, {color: white, size: 40, font_family: 'Courier Prime', weight: normal}]
|
249
|
+
# draw00 = [rectangle, true, 0.08, 0.0, 0.4, 0.40, {color: black}]
|
250
|
+
# draw01 = [rectangle, true, 0.08, 0.40, 0.4, 0.32, {color: blue}]
|
251
|
+
# draw03 = [rectangle, true, 0.08, 0.64, 0.4, 0.12, {color: purple}]
|
252
|
+
# draw04 = [rectangle, true, 0.08, 0.76, 0.4, 0.12, {color: gray}]
|
253
|
+
# draw05 = [rectangle, true, 0.08, 0.88, 0.4, 0.12, {color: brown}]
|
254
|
+
# draw10 = [text, CRuby SQLite3 module, 0.09, 0.18, {color: white, size: 45, font_family: 'Courier Prime', weight: normal}]
|
255
|
+
# draw115= [text, sqlite3_open_v2(3), 0.10, 0.29, {color: yellow, size: 50, font_family: 'Courier Prime', weight: bold}]
|
256
|
+
# draw12 = [text, libsqlite3, 0.18, 0.44, {color: white, size: 50, font_family: 'Courier Prime', weight: normal}]
|
257
|
+
# draw13 = [text, open(2), 0.22, 0.54, {color: yellow, size: 50, font_family: 'Courier Prime', weight: bold}]
|
258
|
+
# draw14 = [text, System call interface, 0.11, 0.67, {color: white, size: 40, font_family: 'Courier Prime', weight: normal}]
|
259
|
+
# draw15 = [text, Device Driver, 0.17, 0.78, {color: white, size: 40, font_family: 'Courier Prime', weight: normal}]
|
260
|
+
# draw16 = [text, Hardware(HDD/SDD), 0.11, 0.89, {color: white, size: 50, font_family: 'Courier Prime', weight: normal}]
|
261
|
+
# draw94 = [rectangle, true, 0.52, 0.0, 0.4, 0.16, {color: red}]
|
262
|
+
# draw95 = [text, SQLite3::Database.open, 0.53, 0.04, {color: white, size: 40, font_family: 'Courier Prime', weight: normal}]
|
263
|
+
# draw20 = [rectangle, true, 0.52, 0.0, 0.4, 0.8, {color: black}]
|
264
|
+
# draw24 = [rectangle, true, 0.52, 0.8, 0.4, 0.20, {color: brown}]
|
265
|
+
# draw30 = [text, PicoRuby SQLite3 class, 0.53, 0.18, {color: white, size: 42, font_family: 'Courier Prime', weight: normal}]
|
266
|
+
# draw31 = [text, sqlite3_open_v2(3), 0.54, 0.29, {color: yellow, size: 50, font_family: 'Courier Prime', weight: bold}]
|
267
|
+
# draw34 = [text, (Ruby)File.open, 0.56, 0.54, {color: yellow, size: 50, font_family: 'Courier Prime', weight: bold}]
|
268
|
+
# draw35 = [text, PicoRuby File class, 0.53, 0.66, {color: white, size: 47, font_family: 'Courier Prime', weight: normal}]
|
269
|
+
# draw36 = [text, Hardware(SD card), 0.55, 0.85, {color: white, size: 50, font_family: 'Courier Prime', weight: normal}]
|
270
|
+
|
271
|
+
= When a Ruby app opens a DB
|
272
|
+
|
273
|
+
# image
|
274
|
+
# src = images/transparent.png
|
275
|
+
# relative-height = 94
|
276
|
+
# relative_margin_top = 2
|
277
|
+
# draw97 = [rectangle, true, 0.08, 0.0, 0.4, 0.16, {color: red}]
|
278
|
+
# draw98 = [text, SQLite3::Database.open, 0.1, 0.04, {color: white, size: 40, font_family: 'Courier Prime', weight: normal}]
|
279
|
+
# draw00 = [rectangle, true, 0.08, 0.0, 0.4, 0.40, {color: black}]
|
280
|
+
# draw01 = [rectangle, true, 0.08, 0.40, 0.4, 0.32, {color: blue}]
|
281
|
+
# draw03 = [rectangle, true, 0.08, 0.64, 0.4, 0.12, {color: purple}]
|
282
|
+
# draw04 = [rectangle, true, 0.08, 0.76, 0.4, 0.12, {color: gray}]
|
283
|
+
# draw05 = [rectangle, true, 0.08, 0.88, 0.4, 0.12, {color: brown}]
|
284
|
+
# draw10 = [text, CRuby SQLite3 module, 0.09, 0.18, {color: white, size: 45, font_family: 'Courier Prime', weight: normal}]
|
285
|
+
# draw115= [text, sqlite3_open_v2(3), 0.10, 0.29, {color: yellow, size: 50, font_family: 'Courier Prime', weight: bold}]
|
286
|
+
# draw12 = [text, libsqlite3, 0.18, 0.44, {color: white, size: 50, font_family: 'Courier Prime', weight: normal}]
|
287
|
+
# draw13 = [text, open(2), 0.22, 0.54, {color: yellow, size: 50, font_family: 'Courier Prime', weight: bold}]
|
288
|
+
# draw14 = [text, System call interface, 0.11, 0.67, {color: white, size: 40, font_family: 'Courier Prime', weight: normal}]
|
289
|
+
# draw15 = [text, Device Driver, 0.17, 0.78, {color: white, size: 40, font_family: 'Courier Prime', weight: normal}]
|
290
|
+
# draw16 = [text, Hardware(HDD/SDD), 0.11, 0.89, {color: white, size: 50, font_family: 'Courier Prime', weight: normal}]
|
291
|
+
# draw94 = [rectangle, true, 0.52, 0.0, 0.4, 0.16, {color: red}]
|
292
|
+
# draw95 = [text, SQLite3::Database.open, 0.53, 0.04, {color: white, size: 40, font_family: 'Courier Prime', weight: normal}]
|
293
|
+
# draw20 = [rectangle, true, 0.52, 0.0, 0.4, 0.8, {color: black}]
|
294
|
+
# draw24 = [rectangle, true, 0.52, 0.8, 0.4, 0.20, {color: brown}]
|
295
|
+
# draw30 = [text, PicoRuby SQLite3 class, 0.53, 0.18, {color: white, size: 42, font_family: 'Courier Prime', weight: normal}]
|
296
|
+
# draw31 = [text, sqlite3_open_v2(3), 0.54, 0.29, {color: yellow, size: 50, font_family: 'Courier Prime', weight: bold}]
|
297
|
+
# draw34 = [text, (Ruby)File.open, 0.56, 0.54, {color: yellow, size: 50, font_family: 'Courier Prime', weight: bold}]
|
298
|
+
# draw35 = [text, PicoRuby File class, 0.53, 0.66, {color: white, size: 47, font_family: 'Courier Prime', weight: normal}]
|
299
|
+
# draw36 = [text, Hardware(SD card), 0.55, 0.85, {color: white, size: 50, font_family: 'Courier Prime', weight: normal}]
|
300
|
+
# draw86 = [text, "WHAT HAPPENS HERE?", 0.49, 0.40, {color: red, size: 55, font_family: 'Montserrat', weight: bold}]
|
301
|
+
# draw88= [rectangle, false, 0.485, 0.28, 0.49, 0.36, {color: red, line_width: 10}]
|
302
|
+
|
303
|
+
= SQLite3 needs VFS
|
304
|
+
|
305
|
+
# image
|
306
|
+
# src = images/transparent.png
|
307
|
+
# relative-height = 94
|
308
|
+
# relative_margin_top = 2
|
309
|
+
# draw97 = [rectangle, true, 0.08, 0.0, 0.4, 0.16, {color: red}]
|
310
|
+
# draw98 = [text, SQLite3::Database.open, 0.1, 0.04, {color: white, size: 40, font_family: 'Courier Prime', weight: normal}]
|
311
|
+
# draw00 = [rectangle, true, 0.08, 0.0, 0.4, 0.40, {color: black}]
|
312
|
+
# draw01 = [rectangle, true, 0.08, 0.40, 0.4, 0.32, {color: blue}]
|
313
|
+
# draw03 = [rectangle, true, 0.08, 0.64, 0.4, 0.12, {color: purple}]
|
314
|
+
# draw04 = [rectangle, true, 0.08, 0.76, 0.4, 0.12, {color: gray}]
|
315
|
+
# draw05 = [rectangle, true, 0.08, 0.88, 0.4, 0.12, {color: brown}]
|
316
|
+
# draw10 = [text, CRuby SQLite3 module, 0.09, 0.18, {color: white, size: 45, font_family: 'Courier Prime', weight: normal}]
|
317
|
+
# draw115= [text, sqlite3_open_v2(3), 0.10, 0.29, {color: yellow, size: 50, font_family: 'Courier Prime', weight: bold}]
|
318
|
+
# draw12 = [text, libsqlite3, 0.18, 0.44, {color: white, size: 50, font_family: 'Courier Prime', weight: normal}]
|
319
|
+
# draw13 = [text, open(2), 0.22, 0.54, {color: yellow, size: 50, font_family: 'Courier Prime', weight: bold}]
|
320
|
+
# draw14 = [text, System call interface, 0.11, 0.67, {color: white, size: 40, font_family: 'Courier Prime', weight: normal}]
|
321
|
+
# draw15 = [text, Device Driver, 0.17, 0.78, {color: white, size: 40, font_family: 'Courier Prime', weight: normal}]
|
322
|
+
# draw16 = [text, Hardware(HDD/SDD), 0.11, 0.89, {color: white, size: 50, font_family: 'Courier Prime', weight: normal}]
|
323
|
+
# draw94 = [rectangle, true, 0.52, 0.0, 0.4, 0.16, {color: red}]
|
324
|
+
# draw95 = [text, SQLite3::Database.open, 0.53, 0.04, {color: white, size: 40, font_family: 'Courier Prime', weight: normal}]
|
325
|
+
# draw20 = [rectangle, true, 0.52, 0.0, 0.4, 0.8, {color: black}]
|
326
|
+
# draw24 = [rectangle, true, 0.52, 0.8, 0.4, 0.20, {color: brown}]
|
327
|
+
# draw30 = [text, PicoRuby SQLite3 class, 0.53, 0.18, {color: white, size: 42, font_family: 'Courier Prime', weight: normal}]
|
328
|
+
# draw31 = [text, sqlite3_open_v2(3), 0.54, 0.29, {color: yellow, size: 50, font_family: 'Courier Prime', weight: bold}]
|
329
|
+
# draw34 = [text, (Ruby)File.open, 0.56, 0.54, {color: yellow, size: 50, font_family: 'Courier Prime', weight: bold}]
|
330
|
+
# draw35 = [text, PicoRuby File class, 0.53, 0.66, {color: white, size: 47, font_family: 'Courier Prime', weight: normal}]
|
331
|
+
# draw36 = [text, Hardware(SD card), 0.55, 0.85, {color: white, size: 50, font_family: 'Courier Prime', weight: normal}]
|
332
|
+
# draw76 = [text, "VFS", 0.45, 0.54, {color: red, size: 75, font_family: 'Montserrat', weight: bold}]
|
333
|
+
# draw78= [rectangle, false, 0.05, 0.51, 0.9, 0.25, {color: red, line_width: 10}]
|
334
|
+
|
335
|
+
= VFS (Virtual FileSystem) in SQLite3
|
336
|
+
|
337
|
+
# image
|
338
|
+
# src = images/vfs1.gif
|
339
|
+
# relative-height = 94
|
340
|
+
# relative_margin_top = 2
|
341
|
+
# relative_margin_left = 10
|
342
|
+
# align = right
|
343
|
+
|
344
|
+
* “The module at the bottom\nof the SQLite implementation\nstack that provides portability\nacross operating systems.”
|
345
|
+
* “ (...) Hence, porting SQLite to\na new operating system is\nsimply a matter of writing a\nnew OS interface layer or\n"VFS".”
|
346
|
+
|
347
|
+
(('tag:xx-small'))(('tag:center'))
|
348
|
+
https://www.sqlite.org/vfs.html
|
349
|
+
|
350
|
+
= Structs to be prepared
|
351
|
+
|
352
|
+
* sqlite3_mem_methods
|
353
|
+
* Pointers to functions to manage ((*memory*))
|
354
|
+
* sqlite3_vfs
|
355
|
+
* Pointers to functions of ((*filesystem*)) management,\n((*utility*)) functions and ((*global app data*))
|
356
|
+
* sqlite3_io_methods
|
357
|
+
* Pointers to functions to handle ((*an open file*))
|
358
|
+
* sqlite3_file (mentioned later)
|
359
|
+
|
360
|
+
= struct sqlite3_mem_methods
|
361
|
+
|
362
|
+
# enscript c
|
363
|
+
struct sqlite3_mem_methods {
|
364
|
+
void *(*xMalloc)(int); /* Memory allocation function */
|
365
|
+
void (*xFree)(void*); /* Free a prior allocation */
|
366
|
+
void *(*xRealloc)(void*,int); /* Resize an allocation */
|
367
|
+
int (*xSize)(void*); /* Return the size of an allocation */
|
368
|
+
int (*xRoundup)(int); /* Round up request size to allocation size */
|
369
|
+
int (*xInit)(void*); /* Initialize the memory allocator */
|
370
|
+
void (*xShutdown)(void*); /* Deinitialize the memory allocator */
|
371
|
+
void *pAppData; /* Argument to xInit() and xShutdown() */
|
372
|
+
};
|
373
|
+
|
374
|
+
= struct sqlite3_vfs
|
375
|
+
|
376
|
+
# enscript c
|
377
|
+
struct sqlite3_vfs {
|
378
|
+
int iVersion; /* Structure version number (currently 3) */
|
379
|
+
int szOsFile; /* Size of subclassed sqlite3_file */
|
380
|
+
int mxPathname; /* Maximum file pathname length */
|
381
|
+
sqlite3_vfs *pNext; /* Next registered VFS */
|
382
|
+
const char *zName; /* Name of this virtual file system */
|
383
|
+
void *pAppData; /* Pointer to application-specific data */
|
384
|
+
int (*xOpen)(sqlite3_vfs*, sqlite3_filename zName, sqlite3_file*,
|
385
|
+
int flags, int *pOutFlags);
|
386
|
+
int (*xDelete)(sqlite3_vfs*, const char *zName, int syncDir);
|
387
|
+
int (*xAccess)(sqlite3_vfs*, const char *zName, int flags, int *pResOut);
|
388
|
+
int (*xFullPathname)(sqlite3_vfs*, const char *zName, int nOut, char *zOut);
|
389
|
+
void *(*xDlOpen)(sqlite3_vfs*, const char *zFilename);
|
390
|
+
void (*xDlError)(sqlite3_vfs*, int nByte, char *zErrMsg);
|
391
|
+
void (*(*xDlSym)(sqlite3_vfs*,void*, const char *zSymbol))(void);
|
392
|
+
void (*xDlClose)(sqlite3_vfs*, void*);
|
393
|
+
int (*xRandomness)(sqlite3_vfs*, int nByte, char *zOut);
|
394
|
+
int (*xSleep)(sqlite3_vfs*, int microseconds);
|
395
|
+
int (*xCurrentTime)(sqlite3_vfs*, double*);
|
396
|
+
int (*xGetLastError)(sqlite3_vfs*, int, char *);
|
397
|
+
int (*xCurrentTimeInt64)(sqlite3_vfs*, sqlite3_int64*);
|
398
|
+
int (*xSetSystemCall)(sqlite3_vfs*, const char *zName, sqlite3_syscall_ptr);
|
399
|
+
sqlite3_syscall_ptr (*xGetSystemCall)(sqlite3_vfs*, const char *zName);
|
400
|
+
const char *(*xNextSystemCall)(sqlite3_vfs*, const char *zName);
|
401
|
+
};
|
402
|
+
|
403
|
+
= struct sqlite3_io_methods
|
404
|
+
|
405
|
+
# enscript c
|
406
|
+
struct sqlite3_io_methods {
|
407
|
+
int iVersion;
|
408
|
+
int (*xClose)(sqlite3_file*);
|
409
|
+
int (*xRead)(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
|
410
|
+
int (*xWrite)(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst);
|
411
|
+
int (*xTruncate)(sqlite3_file*, sqlite3_int64 size);
|
412
|
+
int (*xSync)(sqlite3_file*, int flags);
|
413
|
+
int (*xFileSize)(sqlite3_file*, sqlite3_int64 *pSize);
|
414
|
+
int (*xLock)(sqlite3_file*, int);
|
415
|
+
int (*xUnlock)(sqlite3_file*, int);
|
416
|
+
int (*xCheckReservedLock)(sqlite3_file*, int *pResOut);
|
417
|
+
int (*xFileControl)(sqlite3_file*, int op, void *pArg);
|
418
|
+
int (*xSectorSize)(sqlite3_file*);
|
419
|
+
int (*xDeviceCharacteristics)(sqlite3_file*);
|
420
|
+
int (*xShmMap)(sqlite3_file*, int iPg, int pgsz, int, void volatile**);
|
421
|
+
int (*xShmLock)(sqlite3_file*, int offset, int n, int flags);
|
422
|
+
void (*xShmBarrier)(sqlite3_file*);
|
423
|
+
int (*xShmUnmap)(sqlite3_file*, int deleteFlag);
|
424
|
+
int (*xFetch)(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp);
|
425
|
+
int (*xUnfetch)(sqlite3_file*, sqlite3_int64 iOfst, void *p);
|
426
|
+
};
|
427
|
+
|
428
|
+
= struct sqlite3_file
|
429
|
+
|
430
|
+
# enscript c
|
431
|
+
struct sqlite3_file { /* Defined in sqlite3.h */
|
432
|
+
const struct sqlite3_io_methods *pMethods; /* Methods for an open file */
|
433
|
+
};
|
434
|
+
|
435
|
+
struct PRBFile { /* Defined in PicoRuby */
|
436
|
+
sqlite3_file base;
|
437
|
+
mrbc_vm *vm;
|
438
|
+
mrbc_value *file;
|
439
|
+
char pathname[PATHNAME_MAX_LEN];
|
440
|
+
int sector_size;
|
441
|
+
};
|
442
|
+
|
443
|
+
PRBFile *prbfile = (PRBFile *)pFile;
|
444
|
+
|
445
|
+
(('tag:center'))A trick to let SQLite3 carry Ruby
|
446
|
+
|
447
|
+
= prbVFSOpen is called to open DB
|
448
|
+
|
449
|
+
# enscript c
|
450
|
+
sqlite3_vfs prb_vfs = {
|
451
|
+
...
|
452
|
+
sizeof(PRBFile), /* szOsFile. SQLite3 knows the size of PRBFile👀 */
|
453
|
+
...
|
454
|
+
vm, /* pAppData -> PicoRuby's VM */
|
455
|
+
prbVFSOpen, /* xOpen */
|
456
|
+
...
|
457
|
+
}
|
458
|
+
|
459
|
+
int prbVFSOpen(sqlite3_vfs *pVfs, const char *zName,
|
460
|
+
sqlite3_file *pFile, int flags, int *pOutFlags) {
|
461
|
+
PRBFile *prbfile = (PRBFile *)pFile; /* Cast sqlite3_file to PRBFile */
|
462
|
+
prbfile->vm = prb_vfs.pAppData; /* prb_vfs.pAppData points to VM */
|
463
|
+
pFile->pMethods = &prb_io_methods; /* Attach IO mehtods to the file */
|
464
|
+
prb_file_new(prbfile, zName, flags); /* Wrapper of Ruby's File.new */
|
465
|
+
return SQLITE_OK;
|
466
|
+
}
|
467
|
+
|
468
|
+
= prb_file_new == File.new
|
469
|
+
|
470
|
+
# enscript c
|
471
|
+
int prb_file_new(PRBFile *prbfile, const char *zName, int flags) {
|
472
|
+
mrbc_vm *vm = (mrbc_vm *)prbfile->vm;
|
473
|
+
mrbc_value v[3];
|
474
|
+
v[0] = mrbc_nil_value();
|
475
|
+
v[1] = mrbc_string_new_cstr(vm, zName);
|
476
|
+
char *mode = ... /* "r", "w", "w+", etc. from flags */
|
477
|
+
v[2] = mrbc_string_new_cstr(vm, mode);
|
478
|
+
prb_funcall(vfs_methods.file_new, &v[0], 2);
|
479
|
+
mrbc_decref(&v[1]);
|
480
|
+
mrbc_decref(&v[2]);
|
481
|
+
prbfile->file = mrbc_alloc(vm, sizeof(mrbc_value));
|
482
|
+
memcpy(prbfile->file, &v[0], sizeof(mrbc_value)); /* File Object */
|
483
|
+
return OK;
|
484
|
+
}
|
485
|
+
|
486
|
+
(('tag:center'))
|
487
|
+
((*prb_funcall(vfs_methods.file_new, &v[0], 2)*))🤔
|
488
|
+
|
489
|
+
= prbIORead is called to read DB
|
490
|
+
|
491
|
+
# enscript c
|
492
|
+
sqlite3_io_methods prb_io_methods = {
|
493
|
+
...
|
494
|
+
prbIORead, /* xRead */
|
495
|
+
...
|
496
|
+
}
|
497
|
+
|
498
|
+
int prbIORead(sqlite3_file *pFile, void *zBuf, int iAmt, sqlite3_int64 iOfst) {
|
499
|
+
PRBFile *prbfile = (PRBFile *)pFile;
|
500
|
+
prb_file_read(prbfile, zBuf, iAmt); /* Wrapper of Ruby's File#read */
|
501
|
+
return SQLITE_OK;
|
502
|
+
}
|
503
|
+
|
504
|
+
= prb_file_read == File#read
|
505
|
+
|
506
|
+
# enscript c
|
507
|
+
int prb_file_read(PRBFile *prbfile, void *zBuf, size_t nBuf) {
|
508
|
+
mrbc_value v[2];
|
509
|
+
v[0] = *prbfile->file;
|
510
|
+
v[1] = mrbc_integer_value(nBuf);
|
511
|
+
prb_funcall(vfs_methods.file_read, &v[0], 1);
|
512
|
+
size_t retSize = v[0].string->size;
|
513
|
+
memcpy(zBuf, v[0].string->data, retSize);
|
514
|
+
mrbc_decref(&v[0]);
|
515
|
+
return retSize;
|
516
|
+
}
|
517
|
+
|
518
|
+
(('tag:center'))
|
519
|
+
((*prb_funcall(vfs_methods.file_read, &v[0], 1)*))🤔
|
520
|
+
|
521
|
+
= prb_funcall
|
522
|
+
|
523
|
+
# enscript c
|
524
|
+
void prb_funcall(
|
525
|
+
void (*func)(mrbc_vm *, mrbc_value *, int),
|
526
|
+
mrbc_value *v, int argc
|
527
|
+
)
|
528
|
+
{
|
529
|
+
mrbc_incref(&v[0]);
|
530
|
+
func(prbvfs.pAppData, &v[0], argc);
|
531
|
+
}
|
532
|
+
|
533
|
+
(('tag:center'))
|
534
|
+
Takes a pointer to a C function representing a Ruby method.
|
535
|
+
\n
|
536
|
+
But how does SQLite3 class know File methods?
|
537
|
+
|
538
|
+
= File::VFS class exposes methods😈
|
539
|
+
|
540
|
+
# enscript c
|
541
|
+
void FileVFS_vfs_methods(mrbc_vm *vm, mrbc_value v[], int argc)
|
542
|
+
{
|
543
|
+
prb_vfs_methods vfs_methods = {
|
544
|
+
File_new,
|
545
|
+
File_close,
|
546
|
+
File_read,
|
547
|
+
...
|
548
|
+
};
|
549
|
+
mrbc_value methods = mrbc_instance_new(
|
550
|
+
vm, class_FAT_VFSMethods, sizeof(prb_vfs_methods)
|
551
|
+
);
|
552
|
+
memcpy(methods.instance->data, &vfs_methods, sizeof(prb_vfs_methods));
|
553
|
+
SET_RETURN(methods);
|
554
|
+
}
|
555
|
+
mrbc_define_method(0, class_FAT, "vfs_methods", FileVFS_vfs_methods);
|
556
|
+
|
557
|
+
(('tag:center'))
|
558
|
+
((*SQLite3.vfs_methods = File::VFS.vfs_methods*))
|
559
|
+
|
560
|
+
= From the point of Ruby's view
|
561
|
+
|
562
|
+
# enscript ruby
|
563
|
+
|
564
|
+
SQLite3.vfs_methods = File::VFS.vfs_methods
|
565
|
+
# Pointers to File methods are told to SQLite3
|
566
|
+
|
567
|
+
db = SQLite3::Database.new("database.db")
|
568
|
+
# sqlite3_os_init() -> sqlite3_vfs_register()
|
569
|
+
# sqlite3_open_v2() -> prbVFSOpen() -> prb_file_new() -> File.new
|
570
|
+
|
571
|
+
db.prepare "SELECT * FROM table;"
|
572
|
+
# sqlite3_prepare_v2() -> prbIORead() -> prb_file_read() -> File#read
|
573
|
+
|
574
|
+
(('tag:center'))
|
575
|
+
SQLite3 (Ruby) -> SQLite3 (C) -> File (Ruby)
|
576
|
+
\n
|
577
|
+
In bare-metal (no-OS) PicoRuby,
|
578
|
+
\n
|
579
|
+
methods of File class work as system call
|
580
|
+
|
581
|
+
= chapter
|
582
|
+
(('tag:xx-large:We made it🎉'))
|
583
|
+
== prop
|
584
|
+
: hide-title
|
585
|
+
true
|
586
|
+
|
587
|
+
= SQLite3 in Micon for what? Guess...
|
588
|
+
|
589
|
+
* IoT, General electrical appliance
|
590
|
+
* Store structured sensor data (in case no network)
|
591
|
+
* Log firmware update history
|
592
|
+
* Store, backup and share configuration
|
593
|
+
* Embedded in-memory-database
|
594
|
+
* (CRuby(Ractor[PicoRuby(in-memory-SQLite3) * n]))
|
595
|
+
|
596
|
+
= App for PRK Firmware
|
597
|
+
|
598
|
+
# image
|
599
|
+
# src = images/GPK60-46W_top.jpg
|
600
|
+
# relative-height = 100
|
601
|
+
|
602
|
+
= App for PRK Firmware
|
603
|
+
|
604
|
+
# image
|
605
|
+
# src = images/GPK60-46W.jpg
|
606
|
+
# relative-height = 100
|
607
|
+
# draw0 = [text, SD card, 0.61, 0.62, {color: white, size: 40, font_family: 'Courier Prime', weight: bold}]
|
608
|
+
# draw1 = [text, RTC, 0.18, 0.70, {color: white, size: 40, font_family: 'Courier Prime', weight: bold}]
|
609
|
+
# draw2 = [text, RP2040, 0.45, 0.49, {color: white, size: 40, font_family: 'Courier Prime', weight: bold}]
|
610
|
+
# draw3 = [text, Trackball,0.43, 0.15, {color: white, size: 40, font_family: 'Courier Prime', weight: bold}]
|
611
|
+
|
612
|
+
= chapter
|
613
|
+
(('tag:xx-large:Make a better keymap'))
|
614
|
+
== prop
|
615
|
+
: hide-title
|
616
|
+
true
|
617
|
+
|
618
|
+
= Make a better keymap
|
619
|
+
|
620
|
+
* On the keyboard,
|
621
|
+
* Log every press of the alphabet key with a timestamp
|
622
|
+
* Exclude consecutive same key
|
623
|
+
* On the laptop/desktop,
|
624
|
+
* Analyze the data, then make a better keymap
|
625
|
+
|
626
|
+
= keymap.rb (1/2)
|
627
|
+
|
628
|
+
# enscript Ruby
|
629
|
+
begin
|
630
|
+
dbfile = "/keylog.db"
|
631
|
+
jounalfile = "/keylog.db-journal"
|
632
|
+
if File.exist?(dbfile)
|
633
|
+
File.rename(dbfile, "/keylog-#{Time.now.to_i}.db")
|
634
|
+
File.unlink(jounalfile) if File.exist?(jounalfile)
|
635
|
+
end
|
636
|
+
sqlite3 = SQLite3::Database.new(dbfile)
|
637
|
+
sql = "CREATE TABLE IF NOT EXISTS keylog (
|
638
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
639
|
+
key TEXT, unixtime REAL);"
|
640
|
+
sqlite3.execute(sql)
|
641
|
+
sqlite3_stmt = sqlite3.prepare(
|
642
|
+
"INSERT INTO keylog (key, unixtime) VALUES (?, ?);")
|
643
|
+
rescue => e
|
644
|
+
puts "Not available"
|
645
|
+
puts "#{e.message} (#{e.class})"
|
646
|
+
end
|
647
|
+
|
648
|
+
= keymap.rb (2/2)
|
649
|
+
|
650
|
+
# enscript Ruby
|
651
|
+
|
652
|
+
kbd = Keyboard.new
|
653
|
+
last_keycode = nil
|
654
|
+
kbd.before_report do |keyboard|
|
655
|
+
# KC_A..KC_Z == 4..29
|
656
|
+
if (keycode = keyboard.keycodes[0]) && (keycode < 30)
|
657
|
+
next if last_keycode == keycode
|
658
|
+
last_keycode = keycode
|
659
|
+
key = (keycode + 61).chr # (4 + 61).chr => "A"
|
660
|
+
puts "key: #{key}"
|
661
|
+
begin
|
662
|
+
sqlite3_stmt&.execute(key, Time.now.to_f)
|
663
|
+
rescue => e
|
664
|
+
puts "SQLite3 error: #{e.message} (#{e.class})"
|
665
|
+
end
|
666
|
+
end
|
667
|
+
end
|
668
|
+
|
669
|
+
= keylog.db looks like
|
670
|
+
|
671
|
+
# enscript ruby
|
672
|
+
[[ 1, "H", 1683536977.633],
|
673
|
+
[ 2, "E", 1683536977.8],
|
674
|
+
[ 3, "L", 1683536977.968],
|
675
|
+
[ 4, "O", 1683536978.696],
|
676
|
+
[ 5, "W", 1683536979.16],
|
677
|
+
[ 6, "O", 1683536979.24],
|
678
|
+
[ 7, "R", 1683536979.456,
|
679
|
+
[ 8, "L", 1683536980.008,
|
680
|
+
[ 9, "D", 1683536980.984],
|
681
|
+
[10, "R", 1683536981.616],
|
682
|
+
[11, "U", 1683536983.480],
|
683
|
+
[12, "B", 1683536983.607],
|
684
|
+
[13, "Y", 1683536984.18],
|
685
|
+
(...array goes...)
|
686
|
+
|
687
|
+
= Analysis strategy
|
688
|
+
|
689
|
+
* Order by "most hit key"
|
690
|
+
* Order by "key combination most frequently hit in succession"
|
691
|
+
* Place each character of the combination on opposite sides of the keyboard
|
692
|
+
\nEg: If "RUBY" is the most hit word,
|
693
|
+
\n"RU" -> "R" for right, "U" for left
|
694
|
+
\n"BY" -> "B" for right, "Y" for left
|
695
|
+
|
696
|
+
= Copy keylog.db to laptop, then
|
697
|
+
|
698
|
+
# enscript ruby
|
699
|
+
|
700
|
+
db = SQLite3::Database.new('keylog.db')
|
701
|
+
db.results_as_hash = true
|
702
|
+
db.execute "CREATE TABLE IF NOT EXISTS key_interval
|
703
|
+
(id INTEGER PRIMARY KEY, prev_key TEXT, next_key TEXT, interval REAL);"
|
704
|
+
db.execute "DELETE FROM key_interval;"
|
705
|
+
|
706
|
+
stmt = db.prepare("INSERT INTO key_interval
|
707
|
+
(prev_key, next_key, interval) VALUES (?, ?, ?)")
|
708
|
+
prev_row = nil
|
709
|
+
db.execute("SELECT id, key, unixtime FROM keylog order by id") do |next_row|
|
710
|
+
if prev_row
|
711
|
+
interval = next_row['unixtime'] - prev_row['unixtime']
|
712
|
+
stmt.execute(prev_row['key'], next_row['key'], interval)
|
713
|
+
end
|
714
|
+
prev_row = next_row
|
715
|
+
end
|
716
|
+
|
717
|
+
(('tag:center'))
|
718
|
+
Make another table that consists of the time difference between adjoining keystrokes
|
719
|
+
|
720
|
+
= Prepare objects for result
|
721
|
+
|
722
|
+
# enscript ruby
|
723
|
+
|
724
|
+
ALPHABET = Hash.new.tap do |h|
|
725
|
+
('A'..'Z').each { |c| h[c] = false }
|
726
|
+
end
|
727
|
+
|
728
|
+
COLUMN_SIZE = 5
|
729
|
+
ROW_SIZE = 3
|
730
|
+
KEYMAP_RIGHT = Array.new(COLUMN_SIZE).map do |colomn|
|
731
|
+
colum = Array.new(ROW_SIZE)
|
732
|
+
end
|
733
|
+
KEYMAP_LEFT = Array.new(COLUMN_SIZE).map do |colomn|
|
734
|
+
colum = Array.new(ROW_SIZE)
|
735
|
+
end
|
736
|
+
|
737
|
+
COLUMN_PRIORITY = [1, 2, 0, 3, 4]
|
738
|
+
ROW_PRIORITY = [1, 2, 0]
|
739
|
+
|
740
|
+
ASSIGN_ORDER = Array.new.tap do |a|
|
741
|
+
COLUMN_PRIORITY.each do |column|
|
742
|
+
ROW_PRIORITY.each do |row|
|
743
|
+
a << [column, row]
|
744
|
+
end
|
745
|
+
end
|
746
|
+
end
|
747
|
+
|
748
|
+
= Calculate a better keymap
|
749
|
+
|
750
|
+
# enscript ruby
|
751
|
+
|
752
|
+
right_num, left_num = 0, 0
|
753
|
+
db.execute("SELECT key, COUNT(*) AS count
|
754
|
+
FROM keylog GROUP BY key ORDER BY count DESC;") do |row|
|
755
|
+
key = row["key"]
|
756
|
+
next if ALPHABET[key]
|
757
|
+
db.execute("SELECT next_key, COUNT(*) AS count FROM key_interval
|
758
|
+
WHERE prev_key = '#{key}' AND interval < 1
|
759
|
+
GROUP BY next_key ORDER BY count DESC;") do |row|
|
760
|
+
right_x, right_y = ASSIGN_ORDER[right_num]
|
761
|
+
break if right_x.nil? || right_y.nil?
|
762
|
+
KEYMAP_RIGHT[right_x][right_y] = key
|
763
|
+
ALPHABET[key] = true
|
764
|
+
next_key = row["next_key"]
|
765
|
+
next if ALPHABET[next_key]
|
766
|
+
left_x, left_y = ASSIGN_ORDER[left_num]
|
767
|
+
left_num += 1
|
768
|
+
KEYMAP_LEFT[left_x][left_y] = next_key
|
769
|
+
ALPHABET[next_key] = true
|
770
|
+
break
|
771
|
+
end
|
772
|
+
right_num += 1
|
773
|
+
end
|
774
|
+
db.close
|
775
|
+
|
776
|
+
= Show the result on terminal
|
777
|
+
|
778
|
+
# enscript ruby
|
779
|
+
puts " LEFT HAND RIGHT HAND"
|
780
|
+
0.upto(ROW_SIZE - 1) do |y|
|
781
|
+
(COLUMN_SIZE - 1).downto(0) do |x|
|
782
|
+
print "[#{KEYMAP_LEFT[x][y] || ' '}] "
|
783
|
+
end
|
784
|
+
print " "
|
785
|
+
0.upto(COLUMN_SIZE - 1) do |x|
|
786
|
+
print "[#{KEYMAP_RIGHT[x][y] || ' '}] "
|
787
|
+
end
|
788
|
+
puts
|
789
|
+
end
|
790
|
+
FINGERS = %w(index middle ring pinky)
|
791
|
+
0.upto(4) do |c|
|
792
|
+
FINGERS.reverse.each do |finger|
|
793
|
+
print " #{finger[c] || ' '} "
|
794
|
+
end
|
795
|
+
print " "
|
796
|
+
FINGERS.each do |finger|
|
797
|
+
print " #{finger[c] || ' '} "
|
798
|
+
end
|
799
|
+
puts
|
800
|
+
end
|
801
|
+
|
802
|
+
= The result🎊
|
803
|
+
|
804
|
+
# enscript bash
|
805
|
+
LEFT HAND RIGHT HAND
|
806
|
+
[ ] [ ] [V] [Y] [O] [D] [B] [E] [A] [F]
|
807
|
+
[ ] [M] [T] [K] [R] [P] [L] [I] [C] [N]
|
808
|
+
[ ] [Z] [W] [H] [U] [S] [J] [X] [G] [Q]
|
809
|
+
p r m i i m r p
|
810
|
+
i i i n n i i i
|
811
|
+
n n d d d d n n
|
812
|
+
k g d e e d g k
|
813
|
+
y l x x l y
|
814
|
+
e e
|
815
|
+
|
816
|
+
= The result🎊
|
817
|
+
|
818
|
+
# enscript bash
|
819
|
+
LEFT HAND RIGHT HAND
|
820
|
+
[ ] [ ] [V] [Y] [O] [D] [B] [E] [A] [F]
|
821
|
+
[ ] [M] [T] [K] [R] [P] [L] [I] [C] [N]
|
822
|
+
[ ] [Z] [W] [H] [U] [S] [J] [X] [G] [Q]
|
823
|
+
p r m ☝ ☝ m r p
|
824
|
+
i i i n H,J,K,L n i i i
|
825
|
+
n n d d 🤔 d d n n
|
826
|
+
k g d e e d g k
|
827
|
+
y l x x l y
|
828
|
+
e e
|
829
|
+
|
830
|
+
= ((* *))
|
831
|
+
|
832
|
+
# image
|
833
|
+
# src = images/initial-V.png
|
834
|
+
# relative-width = 100
|
835
|
+
# relative_margin_top = 5
|
836
|
+
|
837
|
+
(('tag:xx-small'))(('tag:center'))
|
838
|
+
https://github.com/tenderlove/initial-v
|
839
|
+
|
840
|
+
== prop
|
841
|
+
: hide-title
|
842
|
+
true
|
843
|
+
|
844
|
+
= chapter
|
845
|
+
(('tag:x-large'))
|
846
|
+
The cursor keys of Vim
|
847
|
+
\n
|
848
|
+
((*H*)), ((*J*)), ((*K*)), ((*L*))
|
849
|
+
\n
|
850
|
+
occupy a prime location😂
|
851
|
+
|
852
|
+
== prop
|
853
|
+
: hide-title
|
854
|
+
true
|
855
|
+
|
856
|
+
= Database's ready, what's next?
|
857
|
+
|
858
|
+
* Improve shell🐚
|
859
|
+
* Network, Socket, TCP
|
860
|
+
* Bluetooth Low Energy🦷
|
861
|
+
* API documentation🔍
|
862
|
+
* Picoるりま(Ruby reference manual)
|
863
|
+
* Write a doujinshi (thin book)📖
|
864
|
+
* In English🇬🇧 🤔
|
865
|
+
|
866
|
+
# image
|
867
|
+
# src = images/picoruby.png
|
868
|
+
# align = right
|
869
|
+
# relative-height = 80
|
870
|
+
# relative_margin_top = -5
|
871
|
+
# relative_margin_left = 18
|
872
|
+
|
873
|
+
= Visit repos and stargaze🌟
|
874
|
+
|
875
|
+
(('tag:small'))
|
876
|
+
\n\n\n\n
|
877
|
+
github.com/picoruby/picoruby
|
878
|
+
\n\n
|
879
|
+
github.com/picoruby/prk_firmware
|
880
|
+
|
881
|
+
# image
|
882
|
+
# src = images/QR_github-com-picoruby.png
|
883
|
+
# align = right
|
884
|
+
# relative-height = 80
|
885
|
+
# relative_margin_top = 0
|
886
|
+
# relative_margin_left = 18
|
887
|
+
|
888
|
+
= chapter
|
889
|
+
(('tag:xx-large'))
|
890
|
+
((*Thank you!!!!q*))
|
891
|
+
== prop
|
892
|
+
: hide-title
|
893
|
+
true
|