rabbit-slide-hasumikin-RubyKaigi2023 2022.05.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|