webring 1.1.2 → 1.1.3

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.
@@ -1,2135 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
3
- <channel>
4
- <title>Jerred&apos;s Blog</title>
5
- <description>My personal blog</description>
6
- <link>https://sjer.red/</link>
7
- <item>
8
- <title>TIL: Using Twoslash with Shiki and Astro</title>
9
- <link>https://sjer.red/blog/til/2024-07-01/</link>
10
- <guid isPermaLink="true">https://sjer.red/blog/til/2024-07-01/</guid>
11
- <description>TIL: Using Twoslash with Shiki and Astro</description>
12
- <pubDate>Mon, 01 Jul 2024 00:00:00 GMT</pubDate>
13
- <content:encoded>&lt;p&gt;Do you want code snippets like below on your Astro site?&lt;/p&gt;
14
- &lt;p&gt;Note: you can hover over types to see their definitions.&lt;/p&gt;
15
- &lt;pre&gt;&lt;code&gt;interface IdLabel {
16
- id: number /* some fields */;
17
- }
18
- interface NameLabel {
19
- name: string /* other fields */;
20
- }
21
- type NameOrId&amp;lt;T extends number | string&amp;gt; = T extends number ? IdLabel :
22
- NameLabel;
23
- // This comment should not be included
24
-
25
- // ---cut---
26
- function createLabel&amp;lt;T extends number | string&amp;gt;(idOrName: T):
27
- NameOrId&amp;lt;T&amp;gt; {
28
- throw &quot;unimplemented&quot;;
29
- }
30
-
31
- let a = createLabel(&quot;typescript&quot;);
32
- &lt;/code&gt;&lt;/pre&gt;
33
- &lt;p&gt;It&apos;s super easy. In your &lt;code&gt;astro.config.ts&lt;/code&gt; file, add
34
- the following:&lt;/p&gt;
35
- &lt;pre&gt;&lt;code&gt;import { defineConfig } from &quot;astro/config&quot;;
36
- // ---cut---
37
- import { rendererRich, transformerTwoslash } from &quot;@shikijs/twoslash&quot;;
38
-
39
- export default defineConfig({
40
- markdown: {
41
- shikiConfig: {
42
- transformers: [
43
- transformerTwoslash({
44
- renderer: rendererRich(),
45
- }),
46
- ],
47
- },
48
- },
49
- });
50
- &lt;/code&gt;&lt;/pre&gt;
51
- &lt;p&gt;Import this CSS in your layout:&lt;/p&gt;
52
- &lt;pre&gt;&lt;code&gt;import &quot;@shikijs/twoslash/style-rich.css&quot;;
53
- &lt;/code&gt;&lt;/pre&gt;
54
- &lt;p&gt;Add the following CSS and import it in your layout:&lt;/p&gt;
55
- &lt;pre&gt;&lt;code&gt;// fixes an issue where type popups are cut off
56
- .astro-code {
57
- overflow: visible !important;
58
- }
59
- &lt;/code&gt;&lt;/pre&gt;
60
- &lt;p&gt;Bonus: enable an automatic light &amp;amp; dark mode. Add the following CSS from
61
- &lt;a
62
- href=&quot;https://shiki.style/guide/dual-themes#query-based-dark-mode&quot;&gt;Shiki&apos;s
63
- documentation&lt;/a&gt;:&lt;/p&gt;
64
- &lt;pre&gt;&lt;code&gt;@media (prefers-color-scheme: dark) {
65
- .shiki,
66
- .shiki span {
67
- color: var(--shiki-dark) !important;
68
- background-color: var(--shiki-dark-bg) !important;
69
- /* Optional, if you also want font styles */
70
- font-style: var(--shiki-dark-font-style) !important;
71
- font-weight: var(--shiki-dark-font-weight) !important;
72
- text-decoration: var(--shiki-dark-text-decoration) !important;
73
- }
74
- }
75
- &lt;/code&gt;&lt;/pre&gt;
76
- &lt;p&gt;Add the following to your &lt;code&gt;astro.config.ts&lt;/code&gt;:&lt;/p&gt;
77
- &lt;pre&gt;&lt;code&gt;import { defineConfig } from &quot;astro/config&quot;;
78
- // ---cut---
79
- export default defineConfig({
80
- markdown: {
81
- shikiConfig: {
82
- theme: &quot;github-dark&quot;,
83
- themes: {
84
- light: &quot;github-light&quot;,
85
- dark: &quot;github-dark&quot;,
86
- },
87
- },
88
- },
89
- });
90
- &lt;/code&gt;&lt;/pre&gt;
91
- &lt;p&gt;You can try it on this site by toggling your browser&apos;s or operating
92
- system&apos;s dark mode.&lt;/p&gt;
93
- &lt;p&gt;Check out &lt;a
94
- href=&quot;https://shikijs.github.io/twoslash/&quot;&gt;Shiki&apos;s Twoslash
95
- documentation&lt;/a&gt; for details.&lt;/p&gt;
96
- </content:encoded>
97
- </item>
98
- <item>
99
- <title>Homelab 1 - Setting up K3s</title>
100
- <link>https://sjer.red/blog/2024/homelab-1/</link>
101
- <guid isPermaLink="true">https://sjer.red/blog/2024/homelab-1/</guid>
102
- <description>Setting up K3s</description>
103
- <pubDate>Wed, 26 Jun 2024 00:00:00 GMT</pubDate>
104
- <content:encoded>&lt;p&gt;This series of posts will detail the setup of my homelab. It goes
105
- into the technical details of how I setup a single-node K3s Kubernetes cluster using cdk8s
106
- and Deno to generate all of the required YAML manifests. This is a practical deployment
107
- with:&lt;/p&gt;
108
- &lt;ul&gt;
109
- &lt;li&gt;Automated backups&lt;/li&gt;
110
- &lt;li&gt;Monitoring and alerting&lt;/li&gt;
111
- &lt;li&gt;Automated deployments&lt;/li&gt;
112
- &lt;li&gt;Automatic image/chart upgrades&lt;/li&gt;
113
- &lt;li&gt;Support for GPU acceleration&lt;/li&gt;
114
- &lt;li&gt;Secure secrets with 1Password&lt;/li&gt;
115
- &lt;li&gt;Secure remote access through Tailscale&lt;/li&gt;
116
- &lt;li&gt;Direct access for game servers and certain protocols like mDNS&lt;/li&gt;
117
- &lt;/ul&gt;
118
- &lt;h2&gt;Table of Contents&lt;/h2&gt;
119
- &lt;h2&gt;Background&lt;/h2&gt;
120
- &lt;p&gt;I&apos;ve had a homelab for around a decade. The hardware itself has gone from
121
- repurposed parts in college (a Core Duo served me very well from 2017-2022) to a very beefy
122
- server today:&lt;/p&gt;
123
- &lt;ul&gt;
124
- &lt;li&gt;&lt;a
125
- href=&quot;https://pcpartpicker.com/product/ZLjRsY/intel-core-i9-14900k-32-ghz-24-core-processor-bx8071514900k&quot;&gt;Intel
126
- Core i9-14900K 3.2 GHz 24-Core&lt;/a&gt;&lt;/li&gt;
127
- &lt;li&gt;2 x &lt;a
128
- href=&quot;https://pcpartpicker.com/product/J2zXsY/corsair-vengeance-32-gb-2-x-16-gb-ddr5-5600-cl40-memory-cmk32gx5m2b5600c40&quot;&gt;Corsair
129
- Vengeance 32 GB (2 x 16 GB) DDR5-5600&lt;/a&gt;&lt;/li&gt;
130
- &lt;li&gt;&lt;a
131
- href=&quot;https://pcpartpicker.com/product/VWxRsY/samsung-990-pro-heatsink-4-tb-m2-2280-pcie-40-x4-nvme-solid-state-drive-mz-v9p4t0cw&quot;&gt;Samsung
132
- 990 Pro 4 TB&lt;/a&gt;&lt;/li&gt;
133
- &lt;li&gt;5 x &lt;a
134
- href=&quot;https://pcpartpicker.com/product/jD3H99/seagate-barracuda-4tb-35-5400rpm-internal-hard-drive-st4000dm004&quot;&gt;Seagate
135
- BarraCuda 4 TB 5400 RPM&lt;/a&gt;&lt;/li&gt;
136
- &lt;/ul&gt;
137
- &lt;p&gt;(The full build is on &lt;a
138
- href=&quot;https://pcpartpicker.com/user/RiotShielder/saved/#view=bnTM3C&quot;&gt;PCPartPicker&lt;/a&gt;)&lt;/p&gt;
139
- &lt;p&gt;Over the years I&apos;ve tried quite a few ways to manage it:&lt;/p&gt;
140
- &lt;ul&gt;
141
- &lt;li&gt;Manually installing everything without any automation&lt;/li&gt;
142
- &lt;li&gt;Artisinal, hand-written bash scripts&lt;/li&gt;
143
- &lt;li&gt;Ansible&lt;/li&gt;
144
- &lt;li&gt;Docker Compose&lt;/li&gt;
145
- &lt;/ul&gt;
146
- &lt;p&gt;Those methods were mostly informed by what I was wanting to learn. That trend
147
- hasn&apos;t changed with my move to Kubernetes. I had no experience with K8s back in
148
- December, and today I use it to manage my homelab quite successfully. Kubernetes is overkill
149
- for a homelab, but it does provide a great learning environment for me where the
150
- consequences are relatively low (as long as my backups keep working).&lt;/p&gt;
151
- &lt;p&gt;I name each iteration of my server so that I can disambiguate between references of
152
- older installations. Previously I named my servers after Greek/Roman gods, but now I&apos;m
153
- using the names of famous computer scientists. The name of the latest iteration is
154
- &quot;lamport&quot;, named after &lt;a
155
- href=&quot;https://en.wikipedia.org/wiki/Leslie_Lamport&quot;&gt;Leslie Lamport&lt;/a&gt;
156
- who is known for his work in distributed systems.&lt;/p&gt;
157
- &lt;h2&gt;Operating System&lt;/h2&gt;
158
- &lt;p&gt;Because I was planning on running everything in Kubernetes, I considered using
159
- &lt;a href=&quot;https://www.talos.dev/&quot;&gt;Talos&lt;/a&gt; rather than my usual choice
160
- of Ubuntu Server. Talos is an immutable Linux distribution with support for Kubernetes
161
- baked-in. I ultimately didn&apos;t choose Talos because at the time required a dedicated
162
- drive for the operating system, meaning my entire 4TB SSD would be taken up by
163
- Talos.&lt;/p&gt;
164
- &lt;p&gt;I ultimately chose Ubuntu Server which has served me well for years despite the
165
- community seeming to be unhappy with some of the recent choices by Canonical. I didn&apos;t
166
- do anything special for my installation other than setting up my RAID 5 array with
167
- &lt;code&gt;mdadm&lt;/code&gt; and &lt;code&gt;fstab&lt;/code&gt;.&lt;/p&gt;
168
- &lt;h2&gt;Kubernetes&lt;/h2&gt;
169
- &lt;p&gt;Similar to how there are multiple flavors of Linux distributions, there are many
170
- distributios of Kubernetes. I chose to use &lt;a
171
- href=&quot;https://k3s.io/&quot;&gt;K3s&lt;/a&gt; since it has a reputation of being
172
- lightweight, easy-to-use, and stable.&lt;/p&gt;
173
- &lt;p&gt;You can configure K3s by creating a file at
174
- &lt;code&gt;/etc/rancher/k3s/config.yaml&lt;/code&gt;. There are some options that
175
- &lt;em&gt;cannot&lt;/em&gt; be changed after K3s is installed. The one most relevant to me
176
- is IPv6 support. Be sure to look through the &lt;a
177
- href=&quot;https://docs.k3s.io/advanced&quot;&gt;configuration options&lt;/a&gt;.&lt;/p&gt;
178
- &lt;p&gt;Installation is straightforward:&lt;/p&gt;
179
- &lt;pre&gt;&lt;code&gt;$ curl -sfL https://get.k3s.io | sh -
180
- &lt;/code&gt;&lt;/pre&gt;
181
- &lt;p&gt;And... that&apos;s it! You now have a Kubernetes cluster running on your machine.
182
- You can use &lt;code&gt;kubectl&lt;/code&gt; to interact with your cluster. I also use &lt;a
183
- href=&quot;https://aptakube.com/&quot;&gt;Aptakube&lt;/a&gt; on my MacBook when I want a GUI
184
- for monitoring my cluster.&lt;/p&gt;
185
- &lt;p&gt;I use &lt;a href=&quot;https://tailscale.com/&quot;&gt;Tailscale&lt;/a&gt; to
186
- securely access my homelab. Tailscale is a Wireguard-based VPN that&apos;s, quite honestly,
187
- fun to use. You can use your favorite VPN to access your homelab, or if you&apos;re brave
188
- you can expose it to the public internet.&lt;/p&gt;
189
- &lt;p&gt;Credentials for your cluster are stored at
190
- &lt;code&gt;/etc/rancher/k3s/k3s.yaml&lt;/code&gt;. You can copy this file to
191
- &lt;code&gt;~/.kube/config&lt;/code&gt; on your local machine to use
192
- &lt;code&gt;kubectl&lt;/code&gt;. Note: you&apos;ll also need to change the
193
- &lt;code&gt;server&lt;/code&gt; field to point to the address of your server.&lt;/p&gt;
194
- &lt;h2&gt;Bootstrapping&lt;/h2&gt;
195
- &lt;p&gt;This does require a small amount of manual bootstrapping, which I describe in my
196
- &lt;a
197
- href=&quot;https://github.com/shepherdjerred/homelab/blob/main/README.md&quot;&gt;repository
198
- README&lt;/a&gt;. Whenever I setup a new cluster/node, I need to:&lt;/p&gt;
199
- &lt;ul&gt;
200
- &lt;li&gt;Install ArgoCD: &lt;code&gt;kubectl apply -n argocd -f
201
- https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml&lt;/code&gt;&lt;/li&gt;
202
- &lt;li&gt;Create a secrets to access my 1Password vaults&lt;/li&gt;
203
- &lt;li&gt;Deploy the manifests in this repo: &lt;code&gt;kubectl apply -f
204
- cdk8s/dist/apps.k8s.yaml&lt;/code&gt;&lt;/li&gt;
205
- &lt;/ul&gt;
206
- &lt;p&gt;I&apos;ve recreated my cluster a couple of times and I&apos;ve been very pleased
207
- with how easy it is to get everything back up and running. It takes me just a few minutes
208
- from installing K3s to having all of my services back up and running.&lt;/p&gt;
209
- &lt;h2&gt;Conclusion&lt;/h2&gt;
210
- &lt;p&gt;I&apos;ve shown how I setup my cluster with K3s. In future posts I&apos;ll
211
- cover:&lt;/p&gt;
212
- &lt;ul&gt;
213
- &lt;li&gt;Using Deno and cdk8s to generate manifests&lt;/li&gt;
214
- &lt;li&gt;Automating deployments with ArgoCD&lt;/li&gt;
215
- &lt;li&gt;Ingress with HTTPS and Tailscale&lt;/li&gt;
216
- &lt;li&gt;Direct connections to pods&lt;/li&gt;
217
- &lt;li&gt;mDNS&lt;/li&gt;
218
- &lt;li&gt;Persistent volumes&lt;/li&gt;
219
- &lt;li&gt;Backups&lt;/li&gt;
220
- &lt;li&gt;Monitoring&lt;/li&gt;
221
- &lt;li&gt;Helm, Kustomize, and operators&lt;/li&gt;
222
- &lt;li&gt;Keeping things up-to-date&lt;/li&gt;
223
- &lt;/ul&gt;
224
- </content:encoded>
225
- </item>
226
- <item>
227
- <title>TIL: Asymmetric Cryptography in Go</title>
228
- <link>https://sjer.red/blog/til/2024-06-05/</link>
229
- <guid isPermaLink="true">https://sjer.red/blog/til/2024-06-05/</guid>
230
- <description>TIL: Asymmetric Cryptography in Go</description>
231
- <pubDate>Wed, 05 Jun 2024 00:00:00 GMT</pubDate>
232
- <content:encoded>&lt;p&gt;I&apos;ve been implementing a feature at work that involves
233
- asymmetric cryptography. It has been a pretty fun exercise in stitching together Go APIs
234
- while reading about best practices.&lt;/p&gt;
235
- &lt;p&gt;Here&apos;s a few things I&apos;ve learned over the last couple of days:&lt;/p&gt;
236
- &lt;ul&gt;
237
- &lt;li&gt;
238
- &lt;p&gt;Go&apos;s cryptography &lt;a
239
- href=&quot;https://kupczynski.info/posts/fips-golang/&quot;&gt;isn&apos;t FIPS
240
- compliant&lt;/a&gt;.&lt;/p&gt;
241
- &lt;/li&gt;
242
- &lt;li&gt;
243
- &lt;p&gt;Go has an implementation of &lt;a
244
- href=&quot;https://pkg.go.dev/crypto/ecdsa&quot;&gt;ECDSA&lt;/a&gt; (Elliptic Curve Digital
245
- Signature Algorithm), but it doesn&apos;t have any elliptic curve asymmetric encryption
246
- algorithms.&lt;/p&gt;
247
- &lt;ul&gt;
248
- &lt;li&gt;The best asymmetric algorithm that Go has is &lt;a
249
- href=&quot;https://pkg.go.dev/crypto/rsa&quot;&gt;RSA&lt;/a&gt;&lt;/li&gt;
250
- &lt;/ul&gt;
251
- &lt;/li&gt;
252
- &lt;li&gt;
253
- &lt;p&gt;Go has an implementation of &lt;a
254
- href=&quot;https://pkg.go.dev/encoding/pem&quot;&gt;PEM&lt;/a&gt; (Privacy Enhanced Mail)
255
- data encoding which can be used to encode public/private in a familiar format. You&apos;ve
256
- probably seen this format with SSH keys:&lt;/p&gt;
257
- &lt;pre&gt;&lt;code&gt;-----BEGIN PUBLIC KEY-----
258
- MIIEpAIBAAKCAQEAuOuUOwNRMbqc0jMEVTOyKuVUu0bk0zD5iwIggBHpDhV58DSJ
259
- SK7OFIFHVMy6FKg2B3Y50srfVJ45OE9Vsb9hfErUNA/PB5meHGEI+yPKeni4GAfy
260
- &amp;lt;and so on&amp;gt;
261
- -----END PUBLIC KEY-----
262
- &lt;/code&gt;&lt;/pre&gt;
263
- &lt;/li&gt;
264
- &lt;li&gt;
265
- &lt;p&gt;The &lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc1421&quot;&gt;legacy PEM
266
- format&lt;/a&gt; has support for plaintext headers like so:&lt;/p&gt;
267
- &lt;pre&gt;&lt;code&gt;-----BEGIN PUBLIC KEY-----
268
- Data: Some value I don&apos;t mind being plaintext
269
- MIIEpAIBAAKCAQEAuOuUOwNRMbqc0jMEVTOyKuVUu0bk0zD5iwIggBHpDhV58DSJ
270
- SK7OFIFHVMy6FKg2B3Y50srfVJ45OE9Vsb9hfErUNA/PB5meHGEI+yPKeni4GAfy
271
- &amp;lt;and so on&amp;gt;
272
- -----END PUBLIC KEY-----
273
- &lt;/code&gt;&lt;/pre&gt;
274
- &lt;ul&gt;
275
- &lt;li&gt;The &lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc7468&quot;&gt;newer
276
- RFC&lt;/a&gt; eplicitly doesn&apos;t support headers, though:
277
- &lt;blockquote&gt;
278
- &lt;p&gt;Unlike legacy PEM encoding &lt;a
279
- href=&quot;https://www.rfc-editor.org/rfc/rfc1421&quot;&gt;RFC1421&lt;/a&gt;, OpenPGP ASCII
280
- armor, and the
281
- OpenSSH key file format, textual encoding does &lt;em&gt;not&lt;/em&gt; define or permit
282
- headers to be encoded alongside the data.&lt;/p&gt;
283
- &lt;/blockquote&gt;
284
- &lt;/li&gt;
285
- &lt;/ul&gt;
286
- &lt;/li&gt;
287
- &lt;li&gt;
288
- &lt;p&gt;Go&apos;s APIs for encrypting, decrypting, signing, and verifying data are quite
289
- pleasant to use!&lt;/p&gt;
290
- &lt;ul&gt;
291
- &lt;li&gt;The &lt;a href=&quot;https://pkg.go.dev/crypto/rsa#pkg-examples&quot;&gt;Go
292
- examples&lt;/a&gt; illustrate this quite well.&lt;/li&gt;
293
- &lt;/ul&gt;
294
- &lt;/li&gt;
295
- &lt;li&gt;
296
- &lt;p&gt;When signing data, Go will first have you run that data through a hash algorithm
297
- (e.g. &lt;a href=&quot;https://pkg.go.dev/crypto/sha256&quot;&gt;SHA256&lt;/a&gt;). This
298
- actually makes quite a bit of sense, and it helps me better understand why secure hashing is
299
- important for cryptography.&lt;/p&gt;
300
- &lt;/li&gt;
301
- &lt;li&gt;
302
- &lt;p&gt;OWASP (Open Worldwide Application Security Project) has a great section on &lt;a
303
- href=&quot;https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html#algorithms&quot;&gt;encryption
304
- algorithms&lt;/a&gt; which can help guide those less familiar with the specifics of
305
- encryption.&lt;/p&gt;
306
- &lt;/li&gt;
307
- &lt;li&gt;
308
- &lt;p&gt;There are a few algorithms for signing and encryption data with RSA. Go implements
309
- PKCS1v15 and OAEP for encryption, and PKCS1v15 and PSS for signing.&lt;/p&gt;
310
- &lt;ul&gt;
311
- &lt;li&gt;This &lt;a
312
- href=&quot;https://security.stackexchange.com/questions/183179/what-is-rsa-oaep-rsa-pss-in-simple-terms/183330#183330&quot;&gt;Stack
313
- Exchange answer&lt;/a&gt; goes into the details of these algorithms.&lt;/li&gt;
314
- &lt;/ul&gt;
315
- &lt;/li&gt;
316
- &lt;/ul&gt;
317
- &lt;p&gt;While I&apos;m generally not a huge fan of Go, I do think the standard library has
318
- some nice packages, and the encryption library is definitely one of them.&lt;/p&gt;
319
- </content:encoded>
320
- </item>
321
- <item>
322
- <title>TIL: Closures in Groovy</title>
323
- <link>https://sjer.red/blog/til/2024-05-24/</link>
324
- <guid isPermaLink="true">https://sjer.red/blog/til/2024-05-24/</guid>
325
- <description>TIL: Closures in Groovy</description>
326
- <pubDate>Fri, 24 May 2024 00:00:00 GMT</pubDate>
327
- <content:encoded>&lt;p&gt;import Socratic from &quot;../../../components/Socratic.astro&quot;;
328
- import Dialog from &quot;../../../components/Dialog.astro&quot;;
329
- import Divider from &quot;../../../components/Divider.astro&quot;;&lt;/p&gt;
330
- &lt;p&gt;I interact with &lt;a href=&quot;https://groovy-lang.org/&quot;&gt;Groovy&lt;/a&gt;
331
- solely because &lt;a href=&quot;https://www.jenkins.io/&quot;&gt;Jenkins&lt;/a&gt; uses it
332
- to define pipelines.&lt;/p&gt;
333
- &lt;p&gt;Pros:&lt;/p&gt;
334
- &lt;ul&gt;
335
- &lt;li&gt;It&apos;s in the Java ecosystem&lt;/li&gt;
336
- &lt;li&gt;It&apos;s a &quot;real&quot; language (turing complete unlike YAML)&lt;/li&gt;
337
- &lt;li&gt;It&apos;s not YAML&lt;/li&gt;
338
- &lt;/ul&gt;
339
- &lt;p&gt;Cons:&lt;/p&gt;
340
- &lt;ul&gt;
341
- &lt;li&gt;No static typing&lt;/li&gt;
342
- &lt;li&gt;Linters/formatters aren&apos;t great&lt;/li&gt;
343
- &lt;li&gt;Jenkins is quite hard to work with and has surprisingly poor tooling&lt;/li&gt;
344
- &lt;/ul&gt;
345
- &lt;p&gt;While updating some Groovy scripts, I wanted to update a utiltiy method to take a
346
- closure in a nicer way. The method looked like this:&lt;/p&gt;
347
- &lt;pre&gt;&lt;code&gt;def hello(clo, first = &quot;John&quot;, last = &quot;Doe&quot;) {
348
- println &quot;Hello, $first $last&quot;
349
- clo()
350
- }
351
-
352
- // usage
353
- foo(clo: { println &quot;I didn&apos;t see you there!&quot; }, first: &quot;Jerred&quot;,
354
- last: &quot;Shepherd&quot;)
355
- &lt;/code&gt;&lt;/pre&gt;
356
- &lt;p&gt;I&apos;ve seen methods that look a bit prettier when called, like this:&lt;/p&gt;
357
- &lt;pre&gt;&lt;code&gt;def bar(someArg, Closure clo) {
358
- clo()
359
- }
360
-
361
- bar(someArg) {
362
- println &apos;hello&apos;
363
- }
364
- &lt;/code&gt;&lt;/pre&gt;
365
- &lt;p&gt;Note that the closure is passed as the last argument. This allows the closure to be
366
- passed in braces after the method call. This looks quite a bit nicer!&lt;/p&gt;
367
- &lt;p&gt;&amp;lt;Dialog&amp;gt;
368
- &amp;lt;Socratic perspective=&quot;student&quot;&amp;gt;
369
- It looks like Groovy has some nice syntatic sugar: named and default parameters are both
370
- pretty nice to have.
371
- &amp;lt;/Socratic&amp;gt;
372
- &amp;lt;Divider /&amp;gt;
373
- &amp;lt;Socratic perspective=&quot;teacher&quot;&amp;gt;
374
- Groovy does have some nice features, but{&quot; &quot;}
375
- &amp;lt;a
376
- href=&quot;https://groovy-lang.org/objectorientation.html#_named_parameters&quot;&amp;gt;named
377
- parameters&amp;lt;/a&amp;gt; have some rough
378
- edges. The documentation doesn&apos;t clearly cover how to use named parameters with default
379
- parameters or closures.
380
- &amp;lt;/Socratic&amp;gt;
381
- &amp;lt;/Dialog&amp;gt;&lt;/p&gt;
382
- &lt;p&gt;Originally, I wanted to combine named parameters, default parameters, and a closure
383
- as the last argument. Unfortunately, this doesn&apos;t seem possible. Here&apos;s what I
384
- came up with:&lt;/p&gt;
385
- &lt;pre&gt;&lt;code&gt;def baz(first = &quot;John&quot;, last = &quot;Doe&quot;, Closure
386
- clo) {
387
- println &quot;Hello, $first $last&quot;
388
- clo()
389
- }
390
- &lt;/code&gt;&lt;/pre&gt;
391
- &lt;p&gt;The result of running this:&lt;/p&gt;
392
- &lt;pre&gt;&lt;code&gt;baz(first: &quot;Jerred&quot;, last: &quot;Shepherd&quot;) {
393
- println &quot;I didn&apos;t see you there!&quot;
394
- }
395
- Hello, [first:Jerred, last:Shepherd] Doe
396
- I didn&apos;t see you there!
397
- &lt;/code&gt;&lt;/pre&gt;
398
- &lt;p&gt;Groovy implements named parameters as a map. Unfortunately, it seems that Groovy is
399
- using the both the &lt;code&gt;first&lt;/code&gt; and &lt;code&gt;last&lt;/code&gt;
400
- parameters as a map value and passing that to the &lt;code&gt;first&lt;/code&gt;
401
- argument.&lt;/p&gt;
402
- &lt;pre&gt;&lt;code&gt;// Groovy takes this:
403
- baz(first: &quot;Jerred&quot;, last: &quot;Shepherd&quot;)
404
-
405
- // and implicitly converts it to this:
406
- baz([first: &quot;Jerred&quot;, last: &quot;Shepherd&quot;], null)
407
-
408
- // so, when calling baz, we&apos;re passing the map as the first argument and null as the
409
- second argument
410
- // this leads to Groovy using the map as the first argument, and the default value of
411
- &quot;Doe&quot; as the second argument
412
- Hello, [first:Jerred, last:Shepherd] Doe
413
- I didn&apos;t see you there!
414
-
415
- // instead, we want Groovy to print:
416
- Hello, Jerred Shepherd
417
- I didn&apos;t see you there!
418
- &lt;/code&gt;&lt;/pre&gt;
419
- &lt;p&gt;Closures are still pretty cool, but it&apos;s frustrating to figure out the syntax
420
- for how some of these features interact. Additionally, the feedback loop for Groovy with
421
- Jenkins is long, so if you don&apos;t know the language, it&apos;s hard to make
422
- progress.&lt;/p&gt;
423
- </content:encoded>
424
- </item>
425
- <item>
426
- <title>Compilers Project Setup</title>
427
- <link>https://sjer.red/blog/2024/compilers-setup/</link>
428
- <guid isPermaLink="true">https://sjer.red/blog/2024/compilers-setup/</guid>
429
- <description>My 8803 Compilers Project Setup</description>
430
- <pubDate>Tue, 23 Jan 2024 00:00:00 GMT</pubDate>
431
- <content:encoded>&lt;p&gt;This semester I&apos;m taking &lt;a
432
- href=&quot;https://omscs.gatech.edu/cs-8803-o08-compilers-theory-and-practice&quot;&gt;CS
433
- 8803 Compilers at Georgia Tech&lt;/a&gt;. I&apos;ve heard this class is quite challenging,
434
- but so far I&apos;ve had a blast and cannot wait to learn more.&lt;/p&gt;
435
- &lt;p&gt;The class has you build a compiler across several phases. It&apos;s unique in that
436
- it sets &lt;em&gt;very&lt;/em&gt; few constraints for your implementation. The only real
437
- technical requirement is that you use C++ and Java, and &lt;a
438
- href=&quot;https://www.antlr.org&quot;&gt;ANTLR&lt;/a&gt; for the front-end.&lt;/p&gt;
439
- &lt;p&gt;All of this freedom leaves a lot of questions for students, especially if
440
- they&apos;re not used to writing large applications by themselves.&lt;/p&gt;
441
- &lt;p&gt;I&apos;m sharing some of the decisions I&apos;ve made for my project. Almost all of
442
- this is specific to Java, so if you&apos;re using C++, you will find little to help
443
- you.&lt;/p&gt;
444
- &lt;p&gt;Let&apos;s get to it! If you have any questions, I&apos;d be happy to answer them.
445
- Shoot me an email: &lt;a
446
- href=&quot;mailto:compilers@sjer.red&quot;&gt;compilers@sjer.red&lt;/a&gt;.&lt;/p&gt;
447
- &lt;h2&gt;Maven&lt;/h2&gt;
448
- &lt;p&gt;You should absolutely be using Maven, especially considering class explicitly
449
- supports it. Maven makes your life &lt;em&gt;much&lt;/em&gt; easier. It handles all of your
450
- dependencies and the lifecycle of building and tests your project.&lt;/p&gt;
451
- &lt;p&gt;Maven is still a fairly relevant technology in the Java world today. It surpassed
452
- &lt;a href=&quot;https://ant.apache.org&quot;&gt;Ant&lt;/a&gt;, and it still a very popular
453
- choice for projects today. The more modern alternative is &lt;a
454
- href=&quot;https://gradle.org&quot;&gt;Gradle&lt;/a&gt;. You might be able to use Gradle,
455
- but you&apos;d have to ask the course staff.&lt;/p&gt;
456
- &lt;p&gt;Here&apos;s my full &lt;a
457
- href=&quot;https://gist.github.com/shepherdjerred/d36b1815f50be9fc2b03f686989987e5&quot;&gt;&lt;code&gt;pom.xml&lt;/code&gt;&lt;/a&gt;
458
- so that you can see how I&apos;ve set all of this up.&lt;/p&gt;
459
- &lt;h2&gt;Earthly&lt;/h2&gt;
460
- &lt;p&gt;&lt;a href=&quot;https://earthly.dev&quot;&gt;Earthly&lt;/a&gt; is one of my
461
- favorite tools. It&apos;s essentially a combination of Docker and Make. You define a set of
462
- targets. Each instruction is run in a containerized environment. This gives you the
463
- simplicity and ergonomics of Make, with all the isolation benefits of Docker.&lt;/p&gt;
464
- &lt;p&gt;I use Earthly to build the &lt;code&gt;.zip&lt;/code&gt; that I submit, and to
465
- ensure that the &lt;code&gt;.zip&lt;/code&gt; is buildable in the Docker environment that
466
- Gradescope uses.&lt;/p&gt;
467
- &lt;p&gt;Here&apos;s what my Earthfile looks like:&lt;/p&gt;
468
- &lt;pre&gt;&lt;code&gt;# This declares the version of Earthly to use
469
- VERSION 0.7
470
-
471
- # This builds the Dockerfile that the course staff provides
472
- image:
473
- FROM DOCKERFILE resources/docker/
474
- WORKDIR /workspace/
475
-
476
- # Download Maven dependencies
477
- deps:
478
- FROM +image
479
- COPY pom.xml .
480
- CACHE /root/.m2/repository/
481
- RUN mvn dependency:resolve
482
- RUN rm pom.xml
483
-
484
- # Zip up my submission. Save the .zip file as a local artifact.
485
- zip:
486
- FROM ubuntu:jammy
487
- WORKDIR /workspace/
488
-
489
- RUN apt update
490
- RUN apt install -y zip
491
-
492
- COPY src/main/ src/main/
493
- COPY pom.xml lombok.config Makefile .
494
- COPY +tiger/Tiger.g4 .
495
- RUN zip -r submission.zip .
496
-
497
- SAVE ARTIFACT submission.zip AS LOCAL submission.zip
498
-
499
- # Unzip my submission an attempt to build it.
500
- build:
501
- FROM +deps
502
- COPY +zip/submission.zip .
503
- RUN unzip submission.zip
504
- RUN make all
505
- SAVE ARTIFACT cs8803_bin/tigerc.jar AS LOCAL cs8803_bin/tigerc.jar
506
-
507
- # I specify my Tiger Lexer and Parser in separate files, within a directory that is idomatic
508
- for ANTLR
509
- # The project PDF says that a single Tiger.g4 file must exist at the root of the project,
510
- which is what this target creates.
511
- tiger:
512
- FROM ubuntu:jammy
513
- COPY src/main/antlr4/com/shepherdjerred/compiler/TigerLexer.g4 .
514
- COPY src/main/antlr4/com/shepherdjerred/compiler/TigerParser.g4 .
515
- # discard the first 5 lines of TigerParser
516
- RUN tail -n +6 TigerParser.g4 &amp;gt; TigerParser.g4
517
- # combine the two files into one
518
- RUN cat TigerLexer.g4 TigerParser.g4 &amp;gt; Tiger.g4
519
- # replace lexer grammar TigerLexer; with grammer Tiger;
520
- RUN sed -i &apos;s/lexer grammar TigerLexer;/grammar Tiger;/g&apos; Tiger.g4
521
- SAVE ARTIFACT Tiger.g4
522
-
523
- # Runs my tests
524
- test:
525
- FROM +deps
526
- COPY src/ src/
527
- COPY lombok.config pom.xml .
528
- RUN mvn test
529
- &lt;/code&gt;&lt;/pre&gt;
530
- &lt;p&gt;To execute an Earthfile, all you need is the &lt;a
531
- href=&quot;https://earthly.dev/get-earthly&quot;&gt;Earthly CLI&lt;/a&gt; and Docker.
532
- Everything else is containerized! For example, I wouldn&apos;t need Java, Maven, or Make to
533
- build this project. If I wanted to set up CI with GitHub Actions, all I would need to do is
534
- use the &lt;a href=&quot;https://github.com/earthly/actions-setup&quot;&gt;Earthly Setup
535
- Action&lt;/a&gt; and then run my target, e.g. &lt;code&gt;earthly
536
- +test&lt;/code&gt;.&lt;/p&gt;
537
- &lt;h2&gt;IDE&lt;/h2&gt;
538
- &lt;p&gt;&lt;a href=&quot;https://www.jetbrains.com/idea/&quot;&gt;IntelliJ IDEA&lt;/a&gt;
539
- is objectively the best Java IDE. If you&apos;re comfortable with VS Code and you don&apos;t
540
- plan to write Java in the future, then sticking with VS Code is okay. If you plan to do more
541
- work in Java, then I would &lt;em&gt;highly&lt;/em&gt; suggest IntelliJ.&lt;/p&gt;
542
- &lt;p&gt;Java has a reputation as a mediocre language. That may be true, but where it shines
543
- is in the ecosystem. IntelliJ is a perfect example of this. It has incredibly powerful
544
- analysis and refactoring capabilities, and very tightly integrates with Java tools like
545
- Maven.&lt;/p&gt;
546
- &lt;p&gt;As an example, I was able to develop my ANTLR grammar using a &lt;a
547
- href=&quot;https://plugins.jetbrains.com/plugin/7358-antlr-v4&quot;&gt;plugin&lt;/a&gt;
548
- which allowed easy testing.&lt;/p&gt;
549
- &lt;p&gt;As a student, you can get a copy of IntelliJ for free with &lt;a
550
- href=&quot;https://www.jetbrains.com/community/education/#students&quot;&gt;JetBrain&apos;s
551
- education program&lt;/a&gt;.&lt;/p&gt;
552
- &lt;h2&gt;Libraries&lt;/h2&gt;
553
- &lt;p&gt;I use a ton of libraries because Java has &lt;em&gt;so&lt;/em&gt; many great
554
- libraries. Here&apos;s what I&apos;m currently using in my project. Some of them
555
- &lt;em&gt;might&lt;/em&gt; be overkill, e.g. Log4J2.&lt;/p&gt;
556
- &lt;ul&gt;
557
- &lt;li&gt;&lt;a href=&quot;https://junit.org/junit5/&quot;&gt;JUnit 5&lt;/a&gt;
558
- &lt;ul&gt;
559
- &lt;li&gt;So much better than the default JUnit 4. I&apos;ll show why in the testing
560
- section.&lt;/li&gt;
561
- &lt;/ul&gt;
562
- &lt;/li&gt;
563
- &lt;li&gt;&lt;a href=&quot;https://projectlombok.org&quot;&gt;Lombok&lt;/a&gt;
564
- &lt;ul&gt;
565
- &lt;li&gt;Generate Java code at compile time. Java is a verbose language — Lombok makes it
566
- better.&lt;/li&gt;
567
- &lt;/ul&gt;
568
- &lt;/li&gt;
569
- &lt;li&gt;&lt;a href=&quot;https://github.com/google/guava&quot;&gt;Guava&lt;/a&gt;
570
- &lt;ul&gt;
571
- &lt;li&gt;I haven&apos;t used this yet, but I often reach for some of its
572
- utilities.&lt;/li&gt;
573
- &lt;/ul&gt;
574
- &lt;/li&gt;
575
- &lt;li&gt;&lt;a href=&quot;https://logging.apache.org/log4j/2.x/&quot;&gt;Log4J2&lt;/a&gt;
576
- &lt;ul&gt;
577
- &lt;li&gt;As mentioned above, I use this for logging.&lt;/li&gt;
578
- &lt;/ul&gt;
579
- &lt;/li&gt;
580
- &lt;li&gt;&lt;a href=&quot;http://immutables.github.io&quot;&gt;Immutables&lt;/a&gt;
581
- &lt;ul&gt;
582
- &lt;li&gt;Generates immutable objects. I prefer a more functional style of programming that
583
- avoids mutation. I suspect this will come in handy in later phases of the project. I&apos;ve
584
- used this both at AWS and in the Distributed Systems course — in both cases, it was quite
585
- helpful.&lt;/li&gt;
586
- &lt;/ul&gt;
587
- &lt;/li&gt;
588
- &lt;li&gt;&lt;a href=&quot;https://picocli.info&quot;&gt;picocli&lt;/a&gt;
589
- &lt;ul&gt;
590
- &lt;li&gt;For my commands. Super easy to use, although you could probably get away with the
591
- native Java libraries.&lt;/li&gt;
592
- &lt;/ul&gt;
593
- &lt;/li&gt;
594
- &lt;/ul&gt;
595
- &lt;h2&gt;Testing&lt;/h2&gt;
596
- &lt;p&gt;Testing the most important aspect of software development. Do yourself a favor and
597
- come up with an effective strategy to test your work now. Having a solid testing methodology
598
- will allow you to quickly verify your work, and will give you confidence that your code
599
- works if you ever go back and refactor.&lt;/p&gt;
600
- &lt;p&gt;This might sound like a lot of work for a school project, but I think this
601
- investment is worth it considering we&apos;re building a compiler for the
602
- semester.&lt;/p&gt;
603
- &lt;p&gt;JUnit is the de facto standard for testing Java. JUnit 4 is the most common version
604
- today, but JUnit 5 has some brilliant features.&lt;/p&gt;
605
- &lt;p&gt;One example is parameterized testing. For example, I have tests that check all the
606
- sample files given to us by the course staff. I use a parameterized test to run the same
607
- test on each file. This is much better than having a separate test for each file, and easily
608
- allows you to add test cases.&lt;/p&gt;
609
- &lt;p&gt;Here&apos;s what a test looks like:&lt;/p&gt;
610
- &lt;pre&gt;&lt;code&gt;public static Stream&amp;lt;String&amp;gt; TestBadLexer() {
611
- var files =
612
- Paths.get(&quot;src/test/resources/official/bad/lexer&quot;).toFile().listFiles();
613
- assert files != null;
614
- return Arrays.stream(files).map(File::getAbsolutePath);
615
- }
616
-
617
- @ParameterizedTest
618
- @MethodSource()
619
- public void TestBadLexer(String file) {
620
- var compiler = new TigerCompiler();
621
- assertThrows(LexerException.class, () -&amp;gt; compiler.compile(file, false));
622
- }
623
- &lt;/code&gt;&lt;/pre&gt;
624
- &lt;p&gt;JUnit will use the static method named &lt;code&gt;TestBadLexer&lt;/code&gt; that
625
- supplies a list of filenames to the &lt;code&gt;TestBadLexer&lt;/code&gt; as the first
626
- argument. My test then checks that the appropriate error is thrown.&lt;/p&gt;
627
- &lt;p&gt;Another great strategy is &lt;a
628
- href=&quot;https://kentcdodds.com/blog/effective-snapshot-testing&quot;&gt;snapshot
629
- testing&lt;/a&gt;. This allows you to save some test output to a file. On subsequent runs,
630
- the test output is compared with the saved output. If the output is different, the test
631
- fails.&lt;/p&gt;
632
- &lt;p&gt;If the test fails, you can visually inspect the different. If the change is
633
- expected, you can update the snapshot. If the change is unexpected, you can investigate
634
- further.&lt;/p&gt;
635
- &lt;p&gt;I use this for investigating the &lt;code&gt;.tokens&lt;/code&gt; file I produce. I
636
- chose to use the &lt;a
637
- href=&quot;https://github.com/origin-energy/java-snapshot-testing&quot;&gt;java-snapshot-testing
638
- library&lt;/a&gt;. It&apos;s not as ergonomic as what I&apos;ve experienced in other
639
- languages like TypeScript and Go, but it does seem to work well.&lt;/p&gt;
640
- &lt;p&gt;Here&apos;s an example test:&lt;/p&gt;
641
- &lt;pre&gt;&lt;code&gt;@Test
642
- @SneakyThrows
643
- public void TestHelloWorldTokens() {
644
- Path tokensFile = Paths.get(&quot;src/test/resources/hello.tokens&quot;);
645
- Files.deleteIfExists(tokensFile);
646
-
647
- var compiler = new TigerCompiler();
648
- var file = &quot;src/test/resources/hello.tiger&quot;;
649
- // the `true` argument tells the compiler to write the tokens to a file
650
- compiler.compile(file, true);
651
-
652
- assert Files.exists(tokensFile);
653
-
654
- expect.toMatchSnapshot(Files.readString(tokensFile));
655
-
656
- Files.delete(tokensFile);
657
- }
658
- &lt;/code&gt;&lt;/pre&gt;
659
- &lt;p&gt;It saves a file named &lt;code&gt;__snapshots__/MainTest.snap&lt;/code&gt;. The
660
- file looks like this:&lt;/p&gt;
661
- &lt;pre&gt;&lt;code&gt;com.shepherdjerred.compiler.MainTest.TestHelloWorldTokens=[
662
- &amp;lt;PROGRAM, &quot;program&quot;&amp;gt;
663
- &amp;lt;ID, &quot;demo_print&quot;&amp;gt;
664
- &amp;lt;LET, &quot;let&quot;&amp;gt;
665
- [the rest of the file goes on]
666
- &lt;/code&gt;&lt;/pre&gt;
667
- &lt;p&gt;Let&apos;s say that I introduce a bug and a token is missing. This is an example of
668
- the error I would see:&lt;/p&gt;
669
- &lt;pre&gt;&lt;code&gt;au.com.origin.snapshots.exceptions.SnapshotMatchException: Error(s)
670
- matching snapshot(s) (1 failure)
671
-
672
- Missing content at line 18:
673
- [&quot;&amp;lt;INT, &quot;int&quot;&amp;gt;&quot;,
674
- &quot;&amp;lt;COMMA, &quot;,&quot;&amp;gt;&quot;,
675
- &quot;&amp;lt;ID, &quot;z&quot;&amp;gt;&quot;]
676
- &lt;/code&gt;&lt;/pre&gt;
677
- &lt;p&gt;I can then investigate why the snapshot no longer matches, and update it if the
678
- changes look correct.&lt;/p&gt;
679
- &lt;p&gt;Here&apos;s my full &lt;a
680
- href=&quot;https://gist.github.com/shepherdjerred/4b13eed30431a91d5ad887932ba923c9&quot;&gt;&lt;code&gt;MainTest.java&lt;/code&gt;&lt;/a&gt;
681
- file so that you can see how I&apos;ve set all of this up.&lt;/p&gt;
682
- &lt;h2&gt;Conclusion&lt;/h2&gt;
683
- &lt;p&gt;I hope this was some use to you, and that you&apos;re as excited as I am for this
684
- semester. If you need any advice about project setup or have some suggestions, feel free to
685
- reach out!&lt;/p&gt;
686
- </content:encoded>
687
- </item>
688
- <item>
689
- <title>Screen Time</title>
690
- <link>https://sjer.red/blog/2023/screen-time/</link>
691
- <guid isPermaLink="true">https://sjer.red/blog/2023/screen-time/</guid>
692
- <description>How I use my phone less</description>
693
- <pubDate>Sun, 01 Oct 2023 00:00:00 GMT</pubDate>
694
- <content:encoded>&lt;h2&gt;Table of Contents&lt;/h2&gt;
695
- &lt;h2&gt;Introduction&lt;/h2&gt;
696
- &lt;p&gt;Like many, I once used my phone quite often. Too often.&lt;/p&gt;
697
- &lt;p&gt;At some point, my usage started feeling out of control and impulsive. I&apos;d pull
698
- out my phone in public, like waiting in line or walking past a stranger. I&apos;d read
699
- through Hacker News and The New York Times both when waking up and before bed. When I felt
700
- bored, I&apos;d find something to distract me. Sometimes, it would be Instagram Reels
701
- (Instagram&apos;s version of TikTok), which would often absorb an hour of my day. At my
702
- worst, I would compulsively open up random apps and check if there was anything new. Slack,
703
- Discord, Email, whatever.&lt;/p&gt;
704
- &lt;p&gt;I view technology like phones and laptops as a tool -- something to help me out. I
705
- don&apos;t want them to rule my life, even if computing is a significant part of my
706
- day-to-day life. I wanted to make my interaction with my phone more intentional.&lt;/p&gt;
707
- &lt;p&gt;I don&apos;t think getting the raw number of hours down is super important, as long
708
- as my interaction and relationship with my phone is intentional usage rather than
709
- compulsive. I want to be more thoughtful about how I use my time and interact with others. I
710
- want to replace shallow interaction on social media with more meaningful, intentional time
711
- spent with those I care about.&lt;/p&gt;
712
- &lt;p&gt;In 2020 and 2021, my screen time increased to around 3 hours daily. At some point,
713
- I decided that I wanted to use my phone less, so I started taking small steps towards that
714
- goal. I wrote this to share what worked for me so others could take away some
715
- ideas.&lt;/p&gt;
716
- &lt;p&gt;This post is centered around iPhones since that&apos;s what I have. That&apos;s
717
- important to note since both platforms have different features and capabilities. Android has
718
- similar features, so most steps should have loose equivalents.&lt;/p&gt;
719
- &lt;p&gt;Please also note that I took the below steps over about two years. It would be
720
- &lt;em&gt;very&lt;/em&gt; hard to do everything all at once. I&apos;ve organized the actions
721
- in order so that the most approachable steps are first and the more extreme ones
722
- last.&lt;/p&gt;
723
- &lt;h2&gt;Keep your Phone Far&lt;/h2&gt;
724
- &lt;p&gt;I used to keep my phone right at my bedside at night.&lt;/p&gt;
725
- &lt;p&gt;Since my phone was by my bed, I would use it every day in the morning and at night.
726
- This would easily eat up an hour of my day. During the day, I kept my phone on my desk or
727
- somewhere near me. This meant it was effortless to quickly check things or be sucked in
728
- because it was too easy to access.&lt;/p&gt;
729
- &lt;p&gt;At night, I moved my phone charger from my nightstand to another room in my house.
730
- This meant that using my phone at night and in the morning would require much more effort.
731
- And, as a bonus, it meant I had to get out of bed to turn off my phone alarm, making it much
732
- less likely that I snooze the alarm and fall back asleep.&lt;/p&gt;
733
- &lt;p&gt;This step represents a general concept that&apos;ll be repeated, making using your
734
- phone harder and requiring more thought. The more effort it takes, the less you will use it.
735
- Ideally, at least for me, I&apos;ll only be using my phone when it&apos;s genuinely
736
- beneficial for me.&lt;/p&gt;
737
- &lt;h2&gt;Track your Screen Time&lt;/h2&gt;
738
- &lt;p&gt;If you don&apos;t already have it enabled, you should turn on &lt;a
739
- href=&quot;https://support.apple.com/en-us/HT208982&quot;&gt;Screen Time&lt;/a&gt;. This
740
- will allow you to track your phone usage and determine what works for you. Additionally, you
741
- can set time limits for specific apps.&lt;/p&gt;
742
- &lt;p&gt;For example, if you use some apps more than you would like, you can give yourself a
743
- daily time limit. When you reach your time limit, iOS will notify you. You&apos;ll be given
744
- the choice to exit the app or override the time limit.&lt;/p&gt;
745
- &lt;p&gt;I would always override the limit, so I configured iOS to require a PIN to override
746
- the time limit. I quickly adapted to just typing in the PIN from memory, so I eventually
747
- used a random PIN that I didn&apos;t know and stored it somewhere. I kept my PIN in
748
- 1Password, although you could use Notes or scribble it on paper and put it in your phone
749
- case.&lt;/p&gt;
750
- &lt;h2&gt;Disable Notifications&lt;/h2&gt;
751
- &lt;p&gt;Notifications are the antithesis of what technology should be. Your phone should
752
- &lt;em&gt;not&lt;/em&gt; be telling you when to look at it -- looking at your phone should
753
- be &lt;em&gt;your&lt;/em&gt; choice.&lt;/p&gt;
754
- &lt;p&gt;Sometimes, notifications are more helpful than harmful, like receiving phone calls
755
- or text messages. I recommend turning off all notifications on your phone, including text
756
- messages.&lt;/p&gt;
757
- &lt;h2&gt;Use a Worse Phone&lt;/h2&gt;
758
- &lt;p&gt;Phones are enticing, which makes you want to use them. By making your phone less
759
- pleasant to use, you&apos;ll want to use it less. The easiest thing you can do is use an
760
- older phone. Don&apos;t upgrade to the latest iPhone; keep the one you have now until it
761
- breaks or no longer receives software updates.&lt;/p&gt;
762
- &lt;p&gt;Aside from that, you can &lt;a
763
- href=&quot;https://www.theverge.com/23637672/grayscale-iphone-android-pixel-samsung-galaxy-how-to&quot;&gt;configure
764
- your iPhone to be in grayscale&lt;/a&gt;. I also enabled a shortcut to toggle between
765
- grayscale and color whenever I triple-clicked the power button to properly view things like
766
- photos sent to me.&lt;/p&gt;
767
- &lt;p&gt;It was incredible how much this helped me. I&apos;d open up Facebook or Instagram
768
- and wouldn&apos;t end up scrolling for 20-30 minutes.&lt;/p&gt;
769
- &lt;p&gt;Lastly, I configured my iPhone to be less bright. You can &lt;a
770
- href=&quot;https://osxdaily.com/2014/03/21/reduce-white-point-ios/&quot;&gt;reduce the white
771
- point&lt;/a&gt; so that your screen is less bright, independently of the brightness slider.
772
- This made the phone very hard to use outdoors, meaning I no longer would pull out my phone
773
- when walking past strangers or doing some outdoor social activity.&lt;/p&gt;
774
- &lt;h2&gt;Delete your Apps&lt;/h2&gt;
775
- &lt;p&gt;Having an app on your phone for every service you use is easy. Things like social
776
- media, email, finances. Many of these apps are pretty convenient to have. That convenience
777
- makes it very easy to get sucked into those apps. Ask yourself if you
778
- &lt;em&gt;really&lt;/em&gt; need these apps on your phone or if you could rely on some other
779
- device like your laptop.&lt;/p&gt;
780
- &lt;p&gt;For instance, I had a bunch of apps that I didn&apos;t need on my phone:&lt;/p&gt;
781
- &lt;ul&gt;
782
- &lt;li&gt;Social Media (Instagram, Facebook, Twitter, Reddit, Discord)&lt;/li&gt;
783
- &lt;li&gt;Finance (Budgeting, Bank apps, Robinhood)&lt;/li&gt;
784
- &lt;li&gt;Email&lt;/li&gt;
785
- &lt;li&gt;Calendar&lt;/li&gt;
786
- &lt;li&gt;Messaging (Telegram, Facebook Messenger)&lt;/li&gt;
787
- &lt;/ul&gt;
788
- &lt;p&gt;I deleted all of the above and now rely on the web equivalents. You don&apos;t
789
- necessarily need to delete everything all at once. Start with just a few apps and slowly
790
- pare down what&apos;s on your phone.&lt;/p&gt;
791
- &lt;p&gt;Deleting messaging apps is probably the most challenging part of this. You&apos;ll
792
- have to let your friends know that you no longer have that app on your phone and that you
793
- should be reached through text (or your messaging app of choice) instead.&lt;/p&gt;
794
- &lt;h2&gt;Switch Browsers&lt;/h2&gt;
795
- &lt;p&gt;Eventually, I ended up without distracting apps on my phone, but unfortunately, I
796
- still had a web browser. After deleting Facebook and Instagram, I started using the web
797
- versions on my phone. I opened those sites way more often than I would&apos;ve
798
- liked.&lt;/p&gt;
799
- &lt;p&gt;iOS doesn&apos;t allow you to delete the Safari browser, meaning there&apos;s no
800
- way to stop yourself from using the web on your iPhone. The next best thing is to use a web
801
- browser that&apos;s harder to use. I switched my default browser from Safari to Firefox
802
- Focus. Firefox Focus doesn&apos;t have the concept of tabs -- you can only have one site
803
- open at a time. It also doesn&apos;t store history, bookmarks, cookies, or anything else,
804
- meaning you must type in whatever site you want to go to and log in every time. This made it
805
- much harder to use Instagram and Facebook, so I went there less often.&lt;/p&gt;
806
- &lt;p&gt;Of course, I still had Safari installed, so I could always open that up, but I
807
- never found myself doing that, so I consider that a win.&lt;/p&gt;
808
- &lt;h2&gt;Delete Social Media Accounts&lt;/h2&gt;
809
- &lt;p&gt;This is hard, and I don&apos;t expect most to do it. For me, it made a lot of
810
- sense. Even after deleting apps from my phone, I scrolled through Facebook, LinkedIn,
811
- Reddit, etc. on my laptop. I didn&apos;t feel like I was getting much value from that
812
- behavior, so I deleted my accounts.&lt;/p&gt;
813
- &lt;p&gt;Facebook is nice for staying in touch with others, especially those who live
814
- further away or whom I don&apos;t talk to often. I decided that, for me, these connections
815
- were not as meaningful. If I want to stay in contact with someone (or vice-versa), we will
816
- find a way. People communicated before Facebook and other social media, so if we care about
817
- each other, it will happen even if it does require more effort.&lt;/p&gt;
818
- &lt;h2&gt;Batch Consumption&lt;/h2&gt;
819
- &lt;p&gt;I&apos;m still experimenting with what to do for news.&lt;/p&gt;
820
- &lt;p&gt;I didn&apos;t want to be disconnected from the world, so I subscribed to the New
821
- York Times and read Hacker News frequently.&lt;/p&gt;
822
- &lt;p&gt;I generally read the New York Times website once or twice a day for the news. That
823
- was a big time sink without any clear value added to my life. I decided to try reading a
824
- physical newspaper by getting the Sunday edition delivered to my house. That reduced my
825
- screen time, but it still wasn&apos;t adding value and was an even bigger time sink because
826
- of how big the paper was. I would spend hours on Sundays reading about things that
827
- didn&apos;t impact me.&lt;/p&gt;
828
- &lt;p&gt;My latest attempt is a subscription to &lt;a
829
- href=&quot;https://www.slow-journalism.com/&quot;&gt;Delayed Gratification&lt;/a&gt;, a
830
- magazine published four times a year with news from the last quarter. Ideally, this means I
831
- read the news only a few days each year, and the stories I read are of higher quality and
832
- importance.&lt;/p&gt;
833
- &lt;p&gt;I&apos;ve found a lot of value in Hacker News. I&apos;ve learned so much from the
834
- site; I could not give it up without a negative impact. At first, I enabled the
835
- &lt;code&gt;noprocrast&lt;/code&gt; feature, which did help, but I felt I could do better.
836
- For a time, I used the &lt;a href=&quot;https://hckrnews.com/&quot;&gt;hckr news
837
- site&lt;/a&gt; to view only the most important stories every day. I was still checking that
838
- site too much, so I moved over to &lt;a
839
- href=&quot;https://hackernewsletter.com/&quot;&gt;Hacker Newsletter&lt;/a&gt;.&lt;/p&gt;
840
- &lt;h2&gt;Conclusion&lt;/h2&gt;
841
- &lt;p&gt;With the steps above, over two years, I&apos;ve reduced my daily screen time from
842
- its peak of about 3 hours to about 15-20 minutes. This varies based on a few factors, like
843
- my mental health. Generally, the more I use my phone, the worse I am. It indicates that
844
- I&apos;m bored or trying to fill time. There are some exceptions to this, like when I&apos;m
845
- intentionally reading some long-form piece on my phone, which I do from time to
846
- time.&lt;/p&gt;
847
- &lt;p&gt;I hope these steps were helpful and can serve as a starting point for those looking
848
- to have a more intentional relationship with technology. Please reach out to me if
849
- you&apos;d like to discuss anything!&lt;/p&gt;
850
- </content:encoded>
851
- </item>
852
- <item>
853
- <title>Writing</title>
854
- <link>https://sjer.red/blog/2023/writing/</link>
855
- <guid isPermaLink="true">https://sjer.red/blog/2023/writing/</guid>
856
- <description>If you can&apos;t write then you can&apos;t think.</description>
857
- <pubDate>Thu, 28 Sep 2023 00:00:00 GMT</pubDate>
858
- <content:encoded>&lt;p&gt;Writing is hard. You&apos;re condensing &lt;em&gt;your&lt;/em&gt;
859
- thoughts into more general concepts that can (hopefully) be understood by others. It&apos;s
860
- equivalent to compressing a giant file (your mind) into some smaller artifact (some
861
- scribbles on a page).&lt;/p&gt;
862
- &lt;p&gt;Writing is valuable. It&apos;s how we can give the gift of knowledge to the future.
863
- One can learn some concept and teach it to others with no investment other than the time
864
- spent compressing that knowledge.&lt;/p&gt;
865
- &lt;p&gt;Writing is introspective and philosophical. Writing delves into the nature of
866
- knowledge. What&apos;s worth sharing with others?&lt;/p&gt;
867
- &lt;p&gt;The process of writing is good practice for thinking. It forces you to focus on
868
- communicating clearly. It makes you think about language and how to effectively convey
869
- concepts. It makes you consider if your examples are truly clear and engaging, or just a
870
- distraction not adding any value.&lt;/p&gt;
871
- &lt;p&gt;I&apos;m really bad at writing. My thoughts are chaotic and disorganized and hard
872
- to communicate. Praciting writing gives me the chance to hone what I&apos;ve been thinking
873
- about, and work &quot;muscles&quot; to help me communicate more clearly.&lt;/p&gt;
874
- </content:encoded>
875
- </item>
876
- <item>
877
- <title>XState</title>
878
- <link>https://sjer.red/blog/2023/xstate/</link>
879
- <guid isPermaLink="true">https://sjer.red/blog/2023/xstate/</guid>
880
- <description>State Machines reduce edge cases</description>
881
- <pubDate>Thu, 14 Sep 2023 00:00:00 GMT</pubDate>
882
- <content:encoded>&lt;p&gt;import { Image } from &quot;astro:assets&quot;;
883
- import channel from &quot;./xstate/channel.png&quot;;
884
- import game from &quot;./xstate/game.png&quot;;
885
- import diagram from &quot;./xstate/diagram.png&quot;;
886
- import simulation from &quot;./xstate/simulation.mp4&quot;;&lt;/p&gt;
887
- &lt;p&gt;My current side project, &lt;a
888
- href=&quot;https://github.com/shepherdjerred/discord-plays-pokemon&quot;&gt;Discord Plays
889
- Pokemon&lt;/a&gt;, has a lot of dependencies. The application streams video with Discord,
890
- which presents a challenge. Discord does not provide APIs for streaming video, and I
891
- didn&apos;t want to have to reverse-engineer the client. I chose to automate interactions
892
- with Discord&apos;s web application using Selenium, which has yielded great results. It can
893
- programmatically stream video to a specific voice channel. This has worked very well so far
894
- -- users are able to play real-time games of Pokemon with each other just using
895
- Discord&apos;s text chat!&lt;/p&gt;
896
- &lt;p&gt;&amp;lt;figure&amp;gt;
897
- &amp;lt;Image src={channel} alt=&quot;A screenshot of Discord text chat with two users
898
- inputting commands&quot; height=&quot;100px&quot; /&amp;gt;
899
- &amp;lt;figcaption&amp;gt;
900
- Two users can input commands at the same time. &lt;code&gt;D&lt;/code&gt; means to simulate
901
- a down-button press once, and &lt;code&gt;10r&lt;/code&gt; means to
902
- simulate a right-button press ten times.&lt;/p&gt;
903
- &lt;pre&gt;&lt;code&gt;If the command is valid, the bot will react to the message with a 👍
904
- once
905
- the command is applied to the game.
906
- &lt;/code&gt;&lt;/pre&gt;
907
- &lt;p&gt;&amp;lt;/figcaption&amp;gt;
908
- &amp;lt;/figure&amp;gt;&lt;/p&gt;
909
- &lt;p&gt;I brute-forced a lot of this code to get the bot working quickly. While it works
910
- well, adding new features was not easy. I wanted only to have the bot stream if a user was
911
- in the voice chat. This requires tracking the state of the bot. Was the bot able to log in
912
- to Discord? Is the bot streaming or not? Has there been any error? How do I switch between
913
- browser tabs since there is one tab for Discord and another for the &lt;a
914
- href=&quot;https://www.emulatorjs.com/&quot;&gt;browser-based emulator&lt;/a&gt;?&lt;/p&gt;
915
- &lt;p&gt;Switching between tabs was an easy problem to fix. I created two instances of
916
- Firefox -- one for the stream and another for the video. This eliminated a whole class of
917
- errors at a slight performance cost.&lt;/p&gt;
918
- &lt;p&gt;&amp;lt;figure&amp;gt;
919
- &amp;lt;Image src={game} alt=&quot;A screenshot of Discord streaming Pokemon&quot; /&amp;gt;
920
- &amp;lt;figcaption&amp;gt;Video is streamed in real-time with instant feedback for the
921
- applied inputs.&amp;lt;/figcaption&amp;gt;
922
- &amp;lt;/figure&amp;gt;&lt;/p&gt;
923
- &lt;p&gt;Tracking the state, however, was not something I wanted to do. There are a lot of
924
- subtle edge cases that I didn&apos;t want to deal with. I felt state machines would be
925
- applicable, but I had never used them in TypeScript.&lt;/p&gt;
926
- &lt;p&gt;I found the &lt;a href=&quot;https://xstate.js.org/&quot;&gt;XState&lt;/a&gt;
927
- project and immediately fell in love. The project is incredibly polished and has excellent
928
- support for VS Code and TypeScript. I ported over my old code to a state machine, although
929
- understanding the concepts that XState introduced took some time.&lt;/p&gt;
930
- &lt;p&gt;&amp;lt;figure&amp;gt;
931
- &amp;lt;Image
932
- src={diagram}
933
- alt=&quot;A screenshot of VS Code with a code pane to the left and a state machine diagram
934
- to the right.&quot;
935
- /&amp;gt;
936
- &amp;lt;figcaption&amp;gt;XState integrates well with VS Code.&amp;lt;/figcaption&amp;gt;
937
- &amp;lt;/figure&amp;gt;&lt;/p&gt;
938
- &lt;p&gt;I was surprised at how helpful the XState VS Code plugin was. It allows me to see a
939
- diagram of my state machine. You can simulate state transitions to understand what your
940
- state machine will do.&lt;/p&gt;
941
- &lt;p&gt;&amp;lt;figure&amp;gt;
942
- &amp;lt;video controls&amp;gt;
943
- &amp;lt;source src={simulation} type=&quot;video/mp4&quot; /&amp;gt;
944
- &amp;lt;/video&amp;gt;
945
- &amp;lt;figcaption&amp;gt;The XState VS Code extension lets you step through your state
946
- transitions.&amp;lt;/figcaption&amp;gt;
947
- &amp;lt;/figure&amp;gt;&lt;/p&gt;
948
- &lt;p&gt;Aside from the coolness of the extension, the library itself is quite polished and
949
- well-documented. Porting over my old code was simple because of how well XState integrates
950
- with promises.&lt;/p&gt;
951
- &lt;p&gt;Here&apos;s an example of the state machine&apos;s state for starting a Discord
952
- video stream. The method in &lt;code&gt;src&lt;/code&gt; is invoked when the
953
- &lt;code&gt;starting_stream&lt;/code&gt; state is reached. Once the promise is complete,
954
- &lt;code&gt;onDone&lt;/code&gt; is called, which transitions the machine to the
955
- &lt;code&gt;streaming&lt;/code&gt; state.&lt;/p&gt;
956
- &lt;p&gt;&amp;lt;figure&amp;gt;&lt;/p&gt;
957
- &lt;pre&gt;&lt;code&gt;// @noErrors
958
- starting_stream: {
959
- invoke: {
960
- src: async ({ driver }, _event) =&amp;gt; {
961
- await joinVoiceChat(driver);
962
- return await shareScreen(driver);
963
- },
964
- onDone: {
965
- target: &quot;streaming&quot;,
966
- },
967
- onError: {
968
- target: &quot;is_error&quot;,
969
- actions: (_context, event) =&amp;gt; {
970
- console.error(event);
971
- },
972
- },
973
- },
974
- },
975
- &lt;/code&gt;&lt;/pre&gt;
976
- &lt;p&gt;&amp;lt;figcaption&amp;gt;The state for starting a Discord
977
- stream.&amp;lt;/figcaption&amp;gt;
978
- &amp;lt;/figure&amp;gt;&lt;/p&gt;
979
- &lt;p&gt;I even wrote some quick unit tests to ensure it works properly. This test was much
980
- easier to write than tests without a state machine.&lt;/p&gt;
981
- &lt;p&gt;&amp;lt;figure&amp;gt;&lt;/p&gt;
982
- &lt;pre&gt;&lt;code&gt;// @noErrors
983
- test(&quot;able to reach the streaming state&quot;, (done) =&amp;gt; {
984
- const actor = interpret(streamMachine)
985
- .onTransition((state) =&amp;gt; {
986
- if (state.matches(&quot;is_ready&quot;)) {
987
- actor.send({ type: &quot;start_stream&quot; });
988
- }
989
- if (state.matches(&quot;is_streaming&quot;)) {
990
- done();
991
- }
992
- });
993
- actor.start();
994
- });
995
- &lt;/code&gt;&lt;/pre&gt;
996
- &lt;p&gt;&amp;lt;figcaption&amp;gt;Unit testing a state machine is straightforward. This
997
- would&apos;ve been a &lt;em&gt;lot&lt;/em&gt; more code without
998
- XState!&amp;lt;/figcaption&amp;gt;
999
- &amp;lt;/figure&amp;gt;&lt;/p&gt;
1000
- &lt;p&gt;Hooking the entire thing up to the application wasn&apos;t hard, either. This
1001
- allows the bot to enter the voice channel and stream only when people are in the
1002
- channel.&lt;/p&gt;
1003
- &lt;p&gt;&amp;lt;figure&amp;gt;&lt;/p&gt;
1004
- &lt;pre&gt;&lt;code&gt;// @noErrors
1005
- const stream = interpret(streamMachine);
1006
-
1007
- stream.start();
1008
-
1009
- handleChannelUpdate(async (channel_count) =&amp;gt; {
1010
- if (channel_count &amp;gt; 0) {
1011
- stream.send({ type: &quot;start_stream&quot; });
1012
- } else {
1013
- stream.send({ type: &quot;end_stream&quot; });
1014
- }
1015
- }
1016
-
1017
- &lt;/code&gt;&lt;/pre&gt;
1018
- &lt;p&gt;&amp;lt;figcaption&amp;gt;A complex set of interactions become so
1019
- easy.&amp;lt;/figcaption&amp;gt;
1020
- &amp;lt;/figure&amp;gt;&lt;/p&gt;
1021
- &lt;p&gt;Overall, using XState feels like a huge win. I can be more confident about how I
1022
- interact with Selenium and Discord. I hope to move more of my application to XState, which
1023
- will significantly help when implementing new input methods and notification
1024
- systems.&lt;/p&gt;
1025
- </content:encoded>
1026
- </item>
1027
- <item>
1028
- <title>Astro</title>
1029
- <link>https://sjer.red/blog/2023/astro/</link>
1030
- <guid isPermaLink="true">https://sjer.red/blog/2023/astro/</guid>
1031
- <description>Astro brings great tooling to static HTML</description>
1032
- <pubDate>Sun, 10 Sep 2023 00:00:00 GMT</pubDate>
1033
- <content:encoded>&lt;p&gt;It&apos;s incredibly easy to find poorly made single-page
1034
- applications (SPA) today. These applications break user expectations by not acting like
1035
- standard webpages. The page title doesn&apos;t update on navigation, the back/forward
1036
- buttons don&apos;t work, the scroll position is not saved, and refreshing the page causes
1037
- surprising behavior.&lt;/p&gt;
1038
- &lt;p&gt;A particularly bad site might be poorly optimized and take a while to load on a
1039
- slow connection or perform poorly when rendered client-side.&lt;/p&gt;
1040
- &lt;p&gt;SPAs tend to be written with libraries like React and Vue. They are great for
1041
- developer productivity, and I find them a joy to use. What I don&apos;t like is the amount
1042
- of work that goes into making them feel like first-class citizens of the web. You have the
1043
- handle the above, and so much more! What about SEO or users that disable
1044
- JavaScript?&lt;/p&gt;
1045
- &lt;p&gt;Many sites do need the interactivity that these libraries provide, but there are
1046
- plenty that don&apos;t. Content-heavy sites, like this one, are mostly read and have little
1047
- client-side interaction that would require JavaScript. You could write the site with plain
1048
- HTML/CSS, but you lose the excellent tooling that React provides. Alternatives include
1049
- server-side rendering with a host like Vercel or static site generation with heavy software
1050
- like Gatsby.&lt;/p&gt;
1051
- &lt;p&gt;I was determined not to compromise. I didn&apos;t want to buy into Vercel and
1052
- certainly didn&apos;t want a super complicated build process with a bunch of
1053
- buy-in.&lt;/p&gt;
1054
- &lt;p&gt;Enter &lt;a href=&quot;https://astro.build/&quot;&gt;Astro&lt;/a&gt;.&lt;/p&gt;
1055
- &lt;p&gt;It&apos;s a perfect fit. By default, it outputs sites with zero JavaScript. Using
1056
- familiar React-ish syntax, you can pass props or call JS at compile time in your components.
1057
- You get the development speed up while also providing sites that perform great and respect
1058
- your users.&lt;/p&gt;
1059
- &lt;p&gt;The feature list is jam-packed, and everything seems to work. The documentation is
1060
- thorough, and it is a joy to use.&lt;/p&gt;
1061
- &lt;p&gt;I migrated this blog over to Astro in a couple of days, and I was able to add a
1062
- bunch of new functionality with little effort, such as generated Open Graph images and an
1063
- RSS feed that updates automatically.&lt;/p&gt;
1064
- &lt;p&gt;The site now has zero client-side JavaScript and does not need a server beyond
1065
- static file hosting. The code is pretty similar to what it was before.&lt;/p&gt;
1066
- &lt;p&gt;I would highly recommend checking out &lt;a
1067
- href=&quot;https://astro.build/&quot;&gt;Astro&lt;/a&gt;. I&apos;m only scratching the
1068
- surface of what you can do with it.&lt;/p&gt;
1069
- </content:encoded>
1070
- </item>
1071
- <item>
1072
- <title>Software Testing</title>
1073
- <link>https://sjer.red/blog/2023/software-testing/</link>
1074
- <guid isPermaLink="true">https://sjer.red/blog/2023/software-testing/</guid>
1075
- <description>Integration tests deserve the most attention</description>
1076
- <pubDate>Fri, 18 Aug 2023 00:00:00 GMT</pubDate>
1077
- <content:encoded>&lt;p&gt;You should verify every desired behavior of your project.&lt;/p&gt;
1078
- &lt;p&gt;Tests exist to verify some behavior of the object being tested. Some tests can be
1079
- manual, such as manually executing a program and verifying that the program works as
1080
- you&apos;d expect. When performing manual testing, the project requirements live in your
1081
- head and aren&apos;t particularly durable. If you return to a project months later, you
1082
- might not know the desired behavior. Additionally, manual testing requires valuable time and
1083
- effort to perform. Manual testing does not scale. As you add more functionality, you will
1084
- need more time, discipline, and the ability to test your project&apos;s behavior
1085
- manually.&lt;/p&gt;
1086
- &lt;p&gt;Automated tests help by explicitly defining the expected behavior, and it provides
1087
- a way to run tests with near-zero manual effort. If the automated tests are easy to run,
1088
- then you can quickly make a change, run the tests, and verify that your change worked as
1089
- expected. If your automated tests have low coverage, you will have less confidence and
1090
- resort to manual testing.&lt;/p&gt;
1091
- &lt;p&gt;Your tests will essentially become the &quot;source of truth&quot; for the expected
1092
- behavior of your project.&lt;/p&gt;
1093
- &lt;p&gt;There are many forms of automated testing, to name a few: unit, integration, and
1094
- end-to-end.&lt;/p&gt;
1095
- &lt;p&gt;What kind of tests you should write largely depends on what type of software
1096
- you&apos;re testing. The classic testing pyramid would suggest that you write many unit
1097
- tests. I think that this is entirely wrong.&lt;/p&gt;
1098
- &lt;blockquote&gt;
1099
- &lt;p&gt;Unit tests can be great if the project is well-architected and if the project is in
1100
- a language that lends itself well to unit testing. I&apos;ve had a great experience with
1101
- unit testing in Java and absolutely terrible experiences with JavaScript.&lt;/p&gt;
1102
- &lt;/blockquote&gt;
1103
- &lt;p&gt;Testing is essential, but that doesn&apos;t mean you should dump unlimited time
1104
- into it. You&apos;ll catch the most bugs by running tests in an environment that is as close
1105
- to production as possible. These are usually called integration tests or end-to-end
1106
- tests.&lt;/p&gt;
1107
- &lt;p&gt;The tradeoff is that being &quot;closer to production&quot; usually means
1108
- &quot;really hard or slow to run&quot;.&lt;/p&gt;
1109
- &lt;p&gt;So, what kind of tests should you write?&lt;/p&gt;
1110
- &lt;p&gt;If the primary purpose of your project is to provide an API, you should write tests
1111
- that check that your API contract is followed to the letter. You might want to have
1112
- performance tests to ensure that it can have the desired response time.&lt;/p&gt;
1113
- &lt;p&gt;If you&apos;re writing a software library, you&apos;ll want many unit tests to
1114
- verify the behavior of the methods you expose. You might want some integration tests, but
1115
- you might be able to get away without any.&lt;/p&gt;
1116
- &lt;p&gt;For web applications, you should skip unit tests. Your application depends on the
1117
- browser, so do your testing in a &lt;em&gt;real&lt;/em&gt; browser. Communicate with
1118
- &lt;em&gt;real&lt;/em&gt; APIs and not mocks. You might want unit tests only for parts of
1119
- your application that are not dependent on the browser or particularly tricky
1120
- behavior.&lt;/p&gt;
1121
- </content:encoded>
1122
- </item>
1123
- <item>
1124
- <title>Debugging C in VS Code</title>
1125
- <link>https://sjer.red/blog/2022/c-debugging-vscode/</link>
1126
- <guid isPermaLink="true">https://sjer.red/blog/2022/c-debugging-vscode/</guid>
1127
- <description>Using VS Code to debug C is pretty easy</description>
1128
- <pubDate>Sun, 23 Oct 2022 00:00:00 GMT</pubDate>
1129
- <content:encoded>&lt;p&gt;Knowing how to use a debugger for the tools that you use is one of
1130
- the best investments you can make.&lt;/p&gt;
1131
- &lt;p&gt;Debuggers help you explore the state of your program. They provide feedback much
1132
- faster than other debugging methods, like print statements. They also allow you to see
1133
- &lt;em&gt;everything&lt;/em&gt;, whereas a print statement will only show what you choose to
1134
- print. This can be very helpful when part of a program that you expect to be working
1135
- correctly is actually misbehaving.&lt;/p&gt;
1136
- &lt;p&gt;Advanced Operating Systems, the course that I&apos;m currently taking for my
1137
- Masters are Georgia Tech, requires that you spend a fair bit of time working in C. C is a
1138
- fine language, but it is very cumbersome to debug programs without a debugger. Save yourself
1139
- some frustration and set your debugging environment up before you start working on your
1140
- program.&lt;/p&gt;
1141
- &lt;p&gt;VS Code makes this very easy.&lt;/p&gt;
1142
- &lt;h2&gt;Setup&lt;/h2&gt;
1143
- &lt;ol&gt;
1144
- &lt;li&gt;
1145
- &lt;p&gt;Install the &lt;a
1146
- href=&quot;https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools&quot;&gt;C/C++
1147
- extension&lt;/a&gt;&lt;/p&gt;
1148
- &lt;/li&gt;
1149
- &lt;li&gt;
1150
- &lt;p&gt;Open up the &quot;Run and Debug&quot; pane&lt;/p&gt;
1151
- &lt;/li&gt;
1152
- &lt;li&gt;
1153
- &lt;p&gt;Click the cog icon to open up the launch.json file which contains your debugging
1154
- configuration&lt;/p&gt;
1155
- &lt;/li&gt;
1156
- &lt;li&gt;
1157
- &lt;p&gt;Copy this into the launch.json file:&lt;/p&gt;
1158
- &lt;pre&gt;&lt;code&gt;{
1159
- &quot;name&quot;: &quot;debug&quot;,
1160
- &quot;type&quot;: &quot;cppdbg&quot;,
1161
- &quot;request&quot;: &quot;launch&quot;,
1162
- &quot;program&quot;: &quot;${workspaceFolder}/&amp;lt;path to your compiled
1163
- binary&amp;gt;&quot;,
1164
- &quot;args&quot;: [&quot;&amp;lt;your program arguments&amp;gt;&quot;],
1165
- &quot;stopAtEntry&quot;: false,
1166
- &quot;cwd&quot;: &quot;${fileDirname}&quot;,
1167
- &quot;environment&quot;: [],
1168
- &quot;externalConsole&quot;: false,
1169
- &quot;MIMode&quot;: &quot;gdb&quot;,
1170
- &quot;setupCommands&quot;: [
1171
- {
1172
- &quot;description&quot;: &quot;Enable pretty-printing for gdb&quot;,
1173
- &quot;text&quot;: &quot;-enable-pretty-printing&quot;,
1174
- &quot;ignoreFailures&quot;: true
1175
- },
1176
- {
1177
- &quot;description&quot;: &quot;Set Disassembly Flavor to Intel&quot;,
1178
- &quot;text&quot;: &quot;-gdb-set disassembly-flavor intel&quot;,
1179
- &quot;ignoreFailures&quot;: true
1180
- }
1181
- ]
1182
- }
1183
- &lt;/code&gt;&lt;/pre&gt;
1184
- &lt;/li&gt;
1185
- &lt;li&gt;
1186
- &lt;p&gt;Save the file and click on &quot;Start Debugging&quot;&lt;/p&gt;
1187
- &lt;/li&gt;
1188
- &lt;/ol&gt;
1189
- &lt;p&gt;VS Code will launch your binary and attach a debuggger. You can do all of the usual
1190
- debugger things like set breakpoints and inspect program state.&lt;/p&gt;
1191
- </content:encoded>
1192
- </item>
1193
- <item>
1194
- <title>Pointer Math in C</title>
1195
- <link>https://sjer.red/blog/2022/c-pointer-math/</link>
1196
- <guid isPermaLink="true">https://sjer.red/blog/2022/c-pointer-math/</guid>
1197
- <description>2D arrays in C can be tricky</description>
1198
- <pubDate>Sat, 15 Oct 2022 00:00:00 GMT</pubDate>
1199
- <content:encoded>&lt;p&gt;C is a very confusing language.&lt;/p&gt;
1200
- &lt;p&gt;The world is built on C, and some people are able to get &lt;em&gt;very&lt;/em&gt;
1201
- good at it, but I am not one of those people.&lt;/p&gt;
1202
- &lt;p&gt;I pick it up for class, learn it, use it, appreciate it, and then forget it. Why
1203
- would I want to write an application in C? I&apos;m not a systems programmer, so I
1204
- don&apos;t touch it.&lt;/p&gt;
1205
- &lt;p&gt;One side effect of this usage pattern is that I quickly forget how pointers and
1206
- manual memory management work.&lt;/p&gt;
1207
- &lt;p&gt;Pointers are the semantics of &lt;code&gt;calloc&lt;/code&gt; and
1208
- &lt;code&gt;free&lt;/code&gt; are easy enough to refresh on. It takes me a little while to
1209
- remember if I need to use &lt;code&gt;*&lt;/code&gt;, &lt;code&gt;&amp;amp;&lt;/code&gt;,
1210
- &lt;code&gt;-&amp;gt;&lt;/code&gt;, or &lt;code&gt;.&lt;/code&gt; when working with
1211
- pointers. That&apos;s not a big deal though -- again, I can refresh myself on the syntax
1212
- rather easily.&lt;/p&gt;
1213
- &lt;p&gt;One thing that does trip me up (and led to a very annoying bug in a barrier
1214
- algorithm) is 2D arrays, or rather representing a 2D array as a pointer.&lt;/p&gt;
1215
- &lt;p&gt;Here&apos;s what I did.&lt;/p&gt;
1216
- &lt;pre&gt;&lt;code&gt;int x;
1217
- int y;
1218
- int *array;
1219
-
1220
- array = calloc(x * y, sizeof(int))
1221
-
1222
- for (int i = 0; i &amp;lt; x; i++) {
1223
- for (int j = 0; j &amp;lt; y; j++&amp;gt;) {
1224
- item = array[i + j];
1225
- }
1226
- }
1227
- &lt;/code&gt;&lt;/pre&gt;
1228
- &lt;p&gt;Now, this seems somewhat reasonable at first. The problem is that there is going to
1229
- be a collision. &lt;code&gt;x = 0, y = 1&lt;/code&gt; and &lt;code&gt;x = 1, y =
1230
- 0&lt;/code&gt; will refer to the same slots in the array, which shouldn&apos;t
1231
- happen!&lt;/p&gt;
1232
- &lt;p&gt;My next attempted was to change the array access to &lt;code&gt;array[i *
1233
- j]&lt;/code&gt;. This also doesn&apos;t work. Consider when &lt;code&gt;i = 0&lt;/code&gt;
1234
- or &lt;code&gt;j = 0&lt;/code&gt;. Any multiplication by zero is zero, so these will all
1235
- refer to the same slot.&lt;/p&gt;
1236
- &lt;p&gt;The correct solution is rather simple. The access should be &lt;code&gt;array[(i *
1237
- y) + j]&lt;/code&gt;. Let&apos;s prove this with an example.&lt;/p&gt;
1238
- &lt;p&gt;With &lt;code&gt;x = 2&lt;/code&gt; and &lt;code&gt;y = 3&lt;/code&gt;. These are
1239
- the possible values of &lt;code&gt;i&lt;/code&gt; and &lt;code&gt;j&lt;/code&gt;:&lt;/p&gt;
1240
- &lt;pre&gt;&lt;code&gt;i = 0, j = 0
1241
- i = 0, j = 1
1242
- i = 0, j = 2
1243
- i = 1, j = 0
1244
- i = 1, j = 1
1245
- i = 1, j = 2
1246
- &lt;/code&gt;&lt;/pre&gt;
1247
- &lt;p&gt;Now, lets see which slot in the array each pair will fit into.&lt;/p&gt;
1248
- &lt;pre&gt;&lt;code&gt;i = 0, j = 0; (0 * 3) + 0 = 0
1249
- i = 0, j = 1; (0 * 3) + 1 = 1
1250
- i = 0, j = 2; (0 * 3) + 2 = 2
1251
- i = 1, j = 0; (1 * 3) + 0 = 3
1252
- i = 1, j = 1; (1 * 3) + 1 = 4
1253
- i = 1, j = 2; (1 * 3) + 2 = 5
1254
- &lt;/code&gt;&lt;/pre&gt;
1255
- &lt;p&gt;A unique index for each item! This is exactly what we wanted.&lt;/p&gt;
1256
- &lt;p&gt;You should perform these accesses based on the usage patterns of your data. The
1257
- example above uses the x value as the column and the y value as the row. Accesses to
1258
- sequential x values will be faster than accesses to sequential y values because of
1259
- locality.&lt;/p&gt;
1260
- </content:encoded>
1261
- </item>
1262
- <item>
1263
- <title>My struggles with C and libvirt</title>
1264
- <link>https://sjer.red/blog/2022/libvirt/</link>
1265
- <guid isPermaLink="true">https://sjer.red/blog/2022/libvirt/</guid>
1266
- <description>Libvirt is pretty unintuitive</description>
1267
- <pubDate>Thu, 06 Oct 2022 00:00:00 GMT</pubDate>
1268
- <content:encoded>&lt;p&gt;I recently completed a project for my Advanced Operating Systems
1269
- course at Georgia Tech. I found that the project was hard not because of the problem itself.
1270
- It&apos;s hard because it must be done in C, using &lt;a
1271
- href=&quot;https://libvirt.org/&quot;&gt;libvirt&lt;/a&gt; APIs which are poorly documented,
1272
- using a test setup that is brittle.&lt;/p&gt;
1273
- &lt;p&gt;This article will hopefully save others some pain.&lt;/p&gt;
1274
- &lt;p&gt;I&apos;ll be providing examples of how to call relevant libvirt APIs, and some
1275
- other useful information&lt;/p&gt;
1276
- &lt;h2&gt;General Tips&lt;/h2&gt;
1277
- &lt;h3&gt;Set up warnings&lt;/h3&gt;
1278
- &lt;p&gt;Before you do anything, enable every single warning that you can for the C
1279
- compiler. The compiler can catch so many mistakes for you if you tell it to. This includes
1280
- issues like implicit casting, incorrect printf calls, ineffectual assignments, and so much
1281
- more.&lt;/p&gt;
1282
- &lt;p&gt;Update your Makefile to enable some warnings, for example:&lt;/p&gt;
1283
- &lt;pre&gt;&lt;code&gt;GCCFLAGS += -Wall -Wextra -Wpedantic \
1284
- -Wformat=2 -Wno-unused-parameter -Wshadow \
1285
- -Wwrite-strings -Wstrict-prototypes -Wold-style-definition \
1286
- -Wredundant-decls -Wnested-externs -Wmissing-include-dirs \
1287
- -Wjump-misses-init -Wlogical-op -std=c11 \
1288
- -Wstrict-overflow -fno-strict-aliasing \
1289
- -Wconversion
1290
-
1291
- compile:
1292
- gcc -g vcpu_scheduler.c -o vcpu_scheduler -lvirt -lm $(GCCFLAGS)
1293
- &lt;/code&gt;&lt;/pre&gt;
1294
- &lt;h3&gt;Refresh on C&lt;/h3&gt;
1295
- &lt;p&gt;I hadn&apos;t written C single my senior year of college. Like many, I&apos;ve been
1296
- spoiled by high-level languages that give you modern luxeries like lists and generics. I
1297
- found that spending some time reading up on modern C and pointers both helped me
1298
- out.&lt;/p&gt;
1299
- &lt;p&gt;I found these resources useful:&lt;/p&gt;
1300
- &lt;ul&gt;
1301
- &lt;li&gt;&lt;a href=&quot;https://matt.sh/howto-c&quot;&gt;How to C&lt;/a&gt;&lt;/li&gt;
1302
- &lt;/ul&gt;
1303
- &lt;h3&gt;Development Environment&lt;/h3&gt;
1304
- &lt;p&gt;The entire class will be scrambling to setup their development environment for the
1305
- first week or so. I personally installed Linux on my desktop and used that to work with
1306
- &lt;a href=&quot;https://code.visualstudio.com/docs/remote/ssh&quot;&gt;VS Code SSH
1307
- Remote&lt;/a&gt;, but there are plenty of other valid ways to work.&lt;/p&gt;
1308
- &lt;h2&gt;C/libvirt patterns&lt;/h2&gt;
1309
- &lt;p&gt;Now, onto some useful patterns.&lt;/p&gt;
1310
- &lt;h2&gt;Counting Host CPUs&lt;/h2&gt;
1311
- &lt;p&gt;C heavily uses output variables. Methods will often return ints that represent
1312
- either a status code (e.g. was there an error or not), or the number of records returned, or
1313
- both.&lt;/p&gt;
1314
- &lt;p&gt;This method returns 0 on success, and -1 on failure.&lt;/p&gt;
1315
- &lt;p&gt;It&apos;s always good to check return codes. Nothing nothing nothing is more
1316
- frustrating than being confused about why straightforward code is failing -- this is one way
1317
- to prevent that from happening.&lt;/p&gt;
1318
- &lt;pre&gt;&lt;code&gt;virNodeInfo node_info;
1319
- if (virNodeGetInfo(conn, &amp;amp;node_info) == -1) {
1320
- exit(1);
1321
- }
1322
- unsigned int num_cpus = node_info.cpus;
1323
- &lt;/code&gt;&lt;/pre&gt;
1324
- &lt;h2&gt;Listing domains&lt;/h2&gt;
1325
- &lt;p&gt;This method takes a pointer. It will allocate a list of domains at the pointer, and
1326
- the return value contains the number of items returned. You can iterate over the items using
1327
- this information.&lt;/p&gt;
1328
- &lt;p&gt;Also, this method requires a bit of cleanup. You didn&apos;t dynamically allocate
1329
- any memory, but the method you called did. Clean up so that you don&apos;t leak
1330
- memory.&lt;/p&gt;
1331
- &lt;pre&gt;&lt;code&gt;virDomainPtr *domain_list;
1332
- int num_domains = virConnectListAllDomains(conn, &amp;amp;domain_list, 0);
1333
- // don&apos;t forget to cleanup!
1334
- for (int i_domain = 0; i_domain &amp;lt; num_domains; i_domain++) {
1335
- virDomainFree(domain_list[i_domain]);
1336
- }
1337
- free(domain_list);
1338
- &lt;/code&gt;&lt;/pre&gt;
1339
- &lt;h2&gt;Pin a vCPU to a pCPU&lt;/h2&gt;
1340
- &lt;p&gt;This one is rough.&lt;/p&gt;
1341
- &lt;p&gt;I think you can form the CPU map manually, but I just call libvirt and let it form
1342
- it for me, then I pin CPUs as I like.&lt;/p&gt;
1343
- &lt;pre&gt;&lt;code&gt;unsigned char *map;
1344
- if (virNodeGetCPUMap(conn, &amp;amp;map, NULL, 0) == -1) {
1345
- exit(1);
1346
- }
1347
-
1348
- // Use CPU 0
1349
- VIR_USE_CPU(map, 0);
1350
- // Don&apos;t use CPU 1
1351
- VIR_UNUSE_CPU(map, 1);
1352
-
1353
- int maplen = VIR_CPU_MAPLEN(number_of_physical_cpus);
1354
-
1355
- // apply these settings to vcpu #0 in the given domain
1356
- virDomainPinVcpu(domain, 0, map, maplen);
1357
- free(map);
1358
- &lt;/code&gt;&lt;/pre&gt;
1359
- </content:encoded>
1360
- </item>
1361
- <item>
1362
- <title>Language Doesn&apos;t Matter</title>
1363
- <link>https://sjer.red/blog/2022/language-doesnt-matter/</link>
1364
- <guid isPermaLink="true">https://sjer.red/blog/2022/language-doesnt-matter/</guid>
1365
- <description>It doesn&apos;t matter what programming language you learn first</description>
1366
- <pubDate>Sun, 01 May 2022 00:00:00 GMT</pubDate>
1367
- <content:encoded>&lt;p&gt;Programming is rife with opinions. Everyone has a favorite IDE,
1368
- framework, paradigm, test framework, and programming language. Engineers are very vocal
1369
- about their preferences and often leave no room for a difference in opinion or even the idea
1370
- that a contender may serve as a reasonable replacement for their favored choice. Before
1371
- someone begins programming they have a myriad of choices to make before starting. The
1372
- biggest of those choices might be what language to learn.&lt;/p&gt;
1373
- &lt;p&gt;Language makes an impact on how one thinks about problems and their solutions. Any
1374
- language is capable of expressing the same idea in multiple distinct ways. Hand the same
1375
- task to two developers who prefer different programming paradigms, say functional vs
1376
- procedural, and they will end up with two completely different-looking programs even if they
1377
- write those programs in the same language.&lt;/p&gt;
1378
- &lt;p&gt;Some programming languages are better than others, especially when framed in the
1379
- context of performing some specific task. Nobody is going to want to write a web application
1380
- in assembly, but assembly is the perfect language to use when milking every ounce of
1381
- performance from a machine. JavaScript is a great language for web applications, but many
1382
- believe that it isn&apos;t suited for desktop application development due to its heavy
1383
- runtime and often poor performance.&lt;/p&gt;
1384
- &lt;p&gt;The other side of the equation is what programming language you know best. C may be
1385
- the best language for the task, but if you&apos;re intimately familiar with Python then it
1386
- makes sense to do a little more work getting your Python program than learning an entirely
1387
- new language (especially if that language is infamous for how difficult it is to correctly
1388
- write).&lt;/p&gt;
1389
- &lt;p&gt;Beginners should learn any general-purpose language that they&apos;re excited about
1390
- unless they have a specific task they want to accomplish. That language might be C++, Java,
1391
- Go, Python, or JavaScript. There may be better, newer languages out there, but that
1392
- doesn&apos;t really matter. Languages are easy to learn after your first (as long as the
1393
- paradigms match).&lt;/p&gt;
1394
- &lt;p&gt;Picking a programming language shouldn&apos;t be a barrier to those entering the
1395
- field. We as engineers should put aside our personal preferences and instead let new
1396
- developers choose whatever language they&apos;re excited about.&lt;/p&gt;
1397
- </content:encoded>
1398
- </item>
1399
- <item>
1400
- <title>Rust is Exciting</title>
1401
- <link>https://sjer.red/blog/2021/rust-is-exciting/</link>
1402
- <guid isPermaLink="true">https://sjer.red/blog/2021/rust-is-exciting/</guid>
1403
- <description>Rust has a lot of potential</description>
1404
- <pubDate>Sat, 19 Jun 2021 00:00:00 GMT</pubDate>
1405
- <content:encoded>&lt;p&gt;Rust is an exciting language. I recently bought The Rust Programming
1406
- Language Book. It&apos;s quite dense with a lot of concepts I haven&apos;t thought about
1407
- since college. Working in high-level programming languages such as Java, Python, and
1408
- TypeScript have allowed me to mostly forget about the woes of low-level programming. Rust
1409
- has both re-introduced me to these problems, and then immediately solved them with the
1410
- advanced static analysis that its compiler provides.&lt;/p&gt;
1411
- &lt;p&gt;I&apos;m still a beginner with Rust. That&apos;s the exciting part. I have so many
1412
- questions; so many things to figure out. It&apos;s a challenge. It&apos;s a lot to learn. It
1413
- reminds me of when I first started programming. There was a mountain of work to do, and an
1414
- endless number of things to figure out. Eventually it gets easier and you become productive.
1415
- You write small applications just because you can — because you want to prove that you know
1416
- what you think you know.&lt;/p&gt;
1417
- &lt;p&gt;Rust is full of features. Many of them deal with safety, such as the ownership
1418
- system and borrow checker. It helps to guarantee memory safety, and coincidentally also
1419
- helps when writing code that will be executed concurrently. These features are important,
1420
- but what I&apos;m really excited about are the language features — the features that make a
1421
- language a joy to work in. Rust has plenty of these. Interoperability with C, pattern
1422
- matching, the lack of a null type, Cargo, immutability-by-default, functional programming
1423
- constructs built-in, macros, a compiler with the most helpful error messages I&apos;ve ever
1424
- seen, tuples, and pattern-matching. I&apos;m just scratching the service. The type system
1425
- and syntax isn&apos;t quite as good as TypeScript, which I hold as the absolute gold
1426
- standard (even if it isn&apos;t perfect).&lt;/p&gt;
1427
- &lt;p&gt;What&apos;s particularly exciting is the applicability of Rust. I tend to use
1428
- Python or Java for small shell scripts/programs. It works great for me, but it&apos;s not
1429
- especially portable. If I want to share my creations with my team members I have to provide
1430
- explicit instructions about which runtime versions must be used, and any possible
1431
- dependencies. Rust is a bit different. Since it produces native binaries I can simply hand
1432
- them the executable (provided the dependencies are bundled) and let them have at
1433
- it.&lt;/p&gt;
1434
- &lt;p&gt;I&apos;m excited to be a beginner again. I&apos;m excited to learn about things
1435
- I&apos;ll never use in my day job. I&apos;m excited to get good at writing Rust code.
1436
- I&apos;m excited for the future of programming languages, which will hopefully follow in
1437
- Rust&apos;s footsteps.&lt;/p&gt;
1438
- </content:encoded>
1439
- </item>
1440
- <item>
1441
- <title>On Perfection</title>
1442
- <link>https://sjer.red/blog/2021/on-perfection/</link>
1443
- <guid isPermaLink="true">https://sjer.red/blog/2021/on-perfection/</guid>
1444
- <description>Being perfect is usually a poor choice</description>
1445
- <pubDate>Sat, 27 Feb 2021 00:00:00 GMT</pubDate>
1446
- <content:encoded>&lt;p&gt;This is a bit of stream of consciousness with some light editing. I
1447
- wrote it quickly on my iPad, so it&apos;s pretty rough. It&apos;s better than nothing
1448
- though.&lt;/p&gt;
1449
- &lt;p&gt;I&apos;ve always thought of myself as a perfectionist especially when it comes to
1450
- the code that I write and the projects that I work on. This tenancy can be useful since it
1451
- has allowed me to dive deep into many topics, but it also limits my productivity. I&apos;ve
1452
- spent so much time learning how to model data and implement data access layer code that I
1453
- never had the opportunity to build anything of consequence with what I learned. I know quite
1454
- a lot about Java and the JVM, but it only comes in handy once in a while.&lt;/p&gt;
1455
- &lt;p&gt;Factorio is one of my favorite games. It&apos;s a game with a simple premise.
1456
- You&apos;re stuck on an alien planet and need to get off of it by building a factory which
1457
- can ultimately produce a spaceship.&lt;/p&gt;
1458
- &lt;p&gt;I found that the game perfectly captures many aspects of programming. One thing
1459
- that it made me realize is the clear trade-off of doing something right versus a
1460
- quick-and-dirty hack to just get some item in my factory on the production line.&lt;/p&gt;
1461
- &lt;p&gt;I&apos;ve noticed that I&apos;m much more critical when reading code not written by
1462
- me. I&apos;ll give myself a pass because I had to get this code shipped today, or because I
1463
- was focused on fixing the real problems. I like writing good code. Not necessarily because
1464
- it has any concrete, intrinsic value, but because writing good code makes me a programmer. I
1465
- do think that there are many advantages to my functional, OOP, data-oriented
1466
- style.&lt;/p&gt;
1467
- &lt;p&gt;It can be especially crippling when I have a deadline and perception that I&apos;m
1468
- unable to do things right. It&apos;s oft said that the best is the enemy of the good, and
1469
- that&apos;s something that I truly struggle with. Why would I do something if I can&apos;t
1470
- do it my way — if I can&apos;t do it perfectly? Obviously I&apos;d do it because it needs to
1471
- be done, but that&apos;s not something I get motivation from. That&apos;s not why I&apos;m a
1472
- programmer. I&apos;m a programmer because I enjoy making things that work, with code that I
1473
- can take pride in.&lt;/p&gt;
1474
- </content:encoded>
1475
- </item>
1476
- <item>
1477
- <title>Introduction</title>
1478
- <link>https://sjer.red/blog/2021/introduction/</link>
1479
- <guid isPermaLink="true">https://sjer.red/blog/2021/introduction/</guid>
1480
- <description>Introducing my blog</description>
1481
- <pubDate>Tue, 12 Jan 2021 00:00:00 GMT</pubDate>
1482
- <content:encoded>&lt;p&gt;A few months ago I discovered fast.ai&apos;s fastbook. It was
1483
- released in August with an accompanying set of YouTube videos that serves as an introductory
1484
- course in the topic of deep learning. The content is made very approachable by the authors
1485
- Jeremy Howard, Rachel Thomas, and Sylvain Gugger.&lt;/p&gt;
1486
- &lt;p&gt;I already had a cursory knowledge of AI having taken a class in college, but I
1487
- wanted to dive deeper into the inner workings — ideally all the way to the metal. I
1488
- purchased the book on Amazon in August, but I&apos;ve been slow to go through it since some
1489
- of the video lectures are rather long, and I learn far better by reading and doing versus
1490
- listening.&lt;/p&gt;
1491
- &lt;p&gt;This week I&apos;ve decided to pick up where I left off. I had been thinking about
1492
- starting a early today (why not), and one of the sections of the book/lecture specifically
1493
- calls out the advantages of starting your own blog which is rather strange for a book about
1494
- deep learning. Anyway, that section of the book pushed me over the edge, so now here we
1495
- are.&lt;/p&gt;
1496
- &lt;p&gt;To give a quick introduction of myself: My name is Jerred Shepherd. I&apos;m a
1497
- software engineer working at Amazon Web Services. I work on problems regarding distributed
1498
- systems and scalability which has been really fun. I love computers and programming, and I
1499
- often spend my free time working on side projects as a hobby.&lt;/p&gt;
1500
- </content:encoded>
1501
- </item>
1502
- <item>
1503
- <title>Intro to 3D Graphics with OpenGL</title>
1504
- <link>https://sjer.red/blog/2019/opengl/</link>
1505
- <guid isPermaLink="true">https://sjer.red/blog/2019/opengl/</guid>
1506
- <description>An introduction to 3D graphics rendering and OpenGL.</description>
1507
- <pubDate>Wed, 03 Apr 2019 00:00:00 GMT</pubDate>
1508
- <content:encoded>&lt;p&gt;import cube from
1509
- &quot;./opengl/img/textured-cube.mp4&quot;;&lt;/p&gt;
1510
- &lt;p&gt;This is an adaptation of my senior &lt;a
1511
- href=&quot;https://github.com/shepherdjerred-homework/seminar-paper&quot;&gt;seminar
1512
- paper&lt;/a&gt;, originally written in LaTeX. You can view the original &lt;a
1513
- href=&quot;/opengl.pdf&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
1514
- &lt;h2&gt;Table of Contents&lt;/h2&gt;
1515
- &lt;h2&gt;Introduction&lt;/h2&gt;
1516
- &lt;p&gt;Computer graphics is an essential component to any consumer facing computer.
1517
- Efficiently rendering computer graphics requires the use of specialized hardware in the form
1518
- of graphics processing units. GPUs work differently than the CPUs which programmers are
1519
- experienced with. This difference is due to the GPUs approach to parallelization. Graphics
1520
- APIs have been created to help programmers write performant and portable code for GPUs. This
1521
- post will introduce the core concepts of 3D graphics rendering and OpenGL, a popular and
1522
- widely-supported graphics API.&lt;/p&gt;
1523
- &lt;p&gt;Computers have become widespread and have touched many aspects of modern-day life.
1524
- From new forms of entertainment such as video games to artificial intelligence, they have
1525
- revolutionized the way to work, learn, and play. Computer graphics in particular have been a
1526
- driving force behind the adoption of computers because they allow users to easily interact
1527
- with computers. Programmers use computer graphics to display user interfaces, render video
1528
- games, and even animate entire films. Every day millions of people interact with interfaces
1529
- on phones, laptops, and other devices that allow them to focus on the work they are doing
1530
- rather than how they communicate with their device.&lt;/p&gt;
1531
- &lt;p&gt;Plenty of tools to manipulate 2D and 3D graphics already exist, such as: 3D object
1532
- editors, image processing applications, UI libraries, and game engines. These tools allow
1533
- programmers to quickly get work done, but they may not know how their work is being
1534
- accomplished. Without this understanding it may be harder for the programmer to debug or
1535
- optimize their code. By learning the lower-level concepts of computer graphics, one is able
1536
- to better use higher-level abstractions and tools.&lt;/p&gt;
1537
- &lt;p&gt;This paper intends to introduce the reader to the broad concepts of graphics
1538
- processing. This includes the purpose of graphics processing units, why graphics APIs exist,
1539
- and an introduction to a commonly used cross-platform graphics API --- OpenGL. OpenGL will
1540
- be discussed so that the reader has an overview of the concepts of OpenGL and can create an
1541
- OpenGL program. This paper will focus on the use OpenGL version 3.2 which is widely
1542
- supported and is very similar to the most recent version of OpenGL, which is 4.7.&lt;/p&gt;
1543
- &lt;h2&gt;Background&lt;/h2&gt;
1544
- &lt;p&gt;Computer graphics is predisposed towards parallel processing due to the repetitive
1545
- and independent nature of the calculations that it requires (p.~4, sellers2016). Graphics
1546
- processing units (GPUs) were created in order to meet the unique hardware requirements of
1547
- computer graphics and are fundamentally different from more traditional central processing
1548
- units (CPUs). While CPUs generally have a few very fast cores, GPUs take another approach by
1549
- having a large number of slow cores. Figure 1 illustrates this difference in architecture.
1550
- It shows the massive difference in arithmetic logic units (ALUs) between CPUs and GPUs.
1551
- These ALUs give GPUs an edge over CPUs when performing mathematical operations and are a
1552
- core part of the parallel processing capability that GPUs possess.&lt;/p&gt;
1553
- &lt;p&gt;&amp;lt;figure&amp;gt;
1554
-
1555
- &amp;lt;figcaption&amp;gt;Figure 1, a comparison of typical CPU and GPU architectures
1556
- (larkin2016).&amp;lt;/figcaption&amp;gt;
1557
- &amp;lt;/figure&amp;gt;&lt;/p&gt;
1558
- &lt;p&gt;A difference also exists between the two in their approach to threading. An
1559
- application running on a CPU may generally have only few threads running due to the high
1560
- cost of creation and context switches. GPUs have lower cost thread creation and can more
1561
- quickly context switch, and they often require tens of thousands of threads in order to
1562
- fully reach their processing potential (larkin2016). Despite the slower performance of an
1563
- individual GPU core, GPUs easily outperform CPUs in tasks related to graphics processing due
1564
- to their parallel processing capabilities. Figure 2 shows the historical theoretical
1565
- performance gap between CPUs and GPUs in giga floating-point operations per second
1566
- (GFLOP/s), which is a common measure of graphical computation speed. The graph shows that
1567
- GPUs have a clear performance advantage, with the gap becoming exponentially larger as time
1568
- goes on.&lt;/p&gt;
1569
- &lt;p&gt;&amp;lt;figure&amp;gt;
1570
-
1571
- &amp;lt;figcaption&amp;gt;
1572
- Figure 2, a performance comparison of CPUs and GPUs in theoretical peak GLFOP/s over 14
1573
- years (galloy2013).
1574
- &amp;lt;/figcaption&amp;gt;
1575
- &amp;lt;/figure&amp;gt;&lt;/p&gt;
1576
- &lt;p&gt;Using GPUs effectively is especially important when rendering user interfaces,
1577
- which generally should remain responsive at all times. Because of the architectural
1578
- differences between GPUs and CPUs, GPUs are programmed differently than CPUs so that their
1579
- peak performance can be reached. In addition to writing high-level code which is compiled
1580
- into assembly, GPUs are also programmed by using APIs that are exposed by the manufacturer
1581
- of the graphics card. GPU manufacturers may support proprietary APIs which is very common on
1582
- cards that are made to be used in systems such as game consoles and arcade machines, or they
1583
- may implement standardized APIs such as OpenGL, Metal, Vulkan, or DirectX.&lt;/p&gt;
1584
- &lt;p&gt;While using a proprietary API may allow a programmer to get more performance out of
1585
- the hardware they are targeting, it will severely limit the application&apos;s portability.
1586
- A program that is written to run on a GPU using OpenGL should be compatible with any other
1587
- GPU implementing the same version of OpenGL. These APIs help programmers to focus on the
1588
- code they are writing rather than what hardware the code will be executed on. The APIs are
1589
- created so that they are at a low enough level that maximum performance can be achieved
1590
- while also being at a high enough level so that programmers can easily use them (p.~4,
1591
- sellers2016).&lt;/p&gt;
1592
- &lt;h2&gt;Graphics Rendering&lt;/h2&gt;
1593
- &lt;p&gt;Computer displays, such as monitors, use a 2D matrix of pixels to display images.
1594
- The dimensions of this matrix are referred to as the display&apos;s resolution.
1595
- Traditionally each pixel of a color display has a red, blue, and green value, which together
1596
- determine the color of the pixel. Graphics rendering is how the color of these pixels are
1597
- determined so that objects such as text, images, and user interfaces can be conveyed
1598
- (mckesson2018). The higher the resolution of a display the more detail that can be shown on
1599
- elements that are drawn. For reference, displays commonly range from standard HD at
1600
- 1280x720, to very high resolutions such as 3840 x 2160, referred to as 4K. Standard HD is
1601
- becoming less common while 4K is slowing gaining market share, especially on high-end
1602
- monitors, TVs, and other consumer electronics.&lt;/p&gt;
1603
- &lt;p&gt;Graphics rendering is done by positioning primitives such as points, lines, and
1604
- triangles in a 3D space. After these primitives are defined, their locations are
1605
- transformed, and they are drawn on the screen through a process called rasterization
1606
- (mckesson2018). Figure 3 shows a simple example of the rasterization process. The figure
1607
- begins by defining a triangle primitive which is composed of three 3D vectors. The
1608
- rasterizer then determines which fragments the triangle overlaps through a process called
1609
- scan conversion. One way to do this is to include pixels if their centers are contained
1610
- within the element. Rasterization then produces list of fragments. A fragment is an element
1611
- that contributes to the final color of a pixel (sellers2016). The fragments are assigned a
1612
- color in later steps of the rendering process, which may be changed even further in the
1613
- process when effects such as lighting are taken into account.&lt;/p&gt;
1614
- &lt;p&gt;Although these three primitives may seem simple and insignificant, they are
1615
- actually the backbone of graphics rendering. Complex 3D models such as those shown in video
1616
- games are really just meshes of many small, connected triangles. Squares and other
1617
- quadrilaterals can be created by connecting two triangles together at their
1618
- hypotenuses.&lt;/p&gt;
1619
- &lt;p&gt;&amp;lt;figure&amp;gt;
1620
- &amp;lt;div class=&quot;bg-white&quot;&amp;gt;&amp;lt;/div&amp;gt;
1621
- &amp;lt;figcaption&amp;gt;Figure 3, a visualization of how a single 2D triangle is
1622
- rasterized (mckesson2018).&amp;lt;/figcaption&amp;gt;
1623
- &amp;lt;/figure&amp;gt;&lt;/p&gt;
1624
- &lt;h3&gt;Matrices&lt;/h3&gt;
1625
- &lt;p&gt;A two-dimensional object can be drawn by simply defining its x and y coordinates.
1626
- Three-dimensional objects will not be drawn correctly because the z coordinate does not
1627
- affect the size of the object. The z coordinate represents how close a point is to a viewer
1628
- with larger numbers being closer, therefore as the z coordinate increases the object should
1629
- increase in size as well. This can be fixed by using a projection matrix and matrix
1630
- multiplication. A projection matrix effects what is visible on the screen, and how it is
1631
- projected onto it (sellers2016). Each (x, y, z, w) coordinate will be multiplied by a 4x4
1632
- matrix, with the result being the location of the coordinate when projected onto the
1633
- display.&lt;/p&gt;
1634
- &lt;p&gt;Before the matrix can be created, the aspect ratio, field of view (FOV), z-near,
1635
- and z-far variables must be determined. The aspect ratio is equal to a display&apos;s width
1636
- in pixels divided by its height in pixels. z-near and z-far represent the closest and
1637
- furthest possible z coordinate values respectively. The FOV is the angle of the scene that
1638
- will be rendered on the screen. Figure 4 illustrates of these four variables. Figure 5 shows
1639
- an example of a 4x4 projection matrix used for 3D rendering. Orthogonal projection matrices
1640
- exist for 2D rendering however they are outside of the scope of this paper.&lt;/p&gt;
1641
- &lt;p&gt;&amp;lt;figure&amp;gt;
1642
-
1643
- &amp;lt;figcaption&amp;gt;
1644
- Figure 4, projection matrix concepts (hernandez2019). The camera represents the position of
1645
- the viewer.
1646
- &amp;lt;/figcaption&amp;gt;
1647
- &amp;lt;/figure&amp;gt;&lt;/p&gt;
1648
- &lt;p&gt;&amp;lt;figure&amp;gt;
1649
-
1650
- &amp;lt;figcaption&amp;gt;
1651
- Figure 5, a projection matrix where a is the aspect ratio, &lt;code&gt;fov&lt;/code&gt; is
1652
- the field of view, &lt;code&gt;z_f&lt;/code&gt; is z-far, and &lt;code&gt;z_n&lt;/code&gt;
1653
- is
1654
- z-near (hernandez2019).
1655
- &amp;lt;/figcaption&amp;gt;
1656
- &amp;lt;/figure&amp;gt;&lt;/p&gt;
1657
- &lt;p&gt;While a projection matrix defines how all rendered geometry appears, a model matrix
1658
- can be used to transform individual elements. Like a projection matrix, a model matrix is a
1659
- 4v4 matrix. It is used to translate, rotate, and scale individual objects on the screen.
1660
- Model matrices are very important in 3D rendering, as they allow objects to be positioned
1661
- precisely in scenes without having to manually calculate vertex locations. Like the
1662
- projection matrix, the model matrix is applied by multiplying the position of each vertex by
1663
- the matrix. The result is the new position of the vertex with translation, rotation, and
1664
- scaling applied.&lt;/p&gt;
1665
- &lt;h2&gt;OpenGL&lt;/h2&gt;
1666
- &lt;p&gt;OpenGL is both a specification and a graphics API which is commonly implemented on
1667
- modern graphics cards. The specification defines how the use of API should affect the
1668
- graphics card. Like other graphics APIs, this allows users to use the underlying graphics
1669
- hardware portably. OpenGL&apos;s development began at a computer hardware manufacturer named
1670
- Silicon Graphics Inc. (SGI). SGI created its own proprietary graphics API named IrisGL which
1671
- was used on its workstations and graphics hardware. IrisGL was cleaned up and formalized
1672
- into OpenGL. The first version of OpenGL was released to the public as version 1.0 in 1992
1673
- (openglwiki2018). This initial version has been revised many times, with the latest version
1674
- being 4.6, which was released on July 31st, 2017 (openglwiki2018).&lt;/p&gt;
1675
- &lt;p&gt;API bindings exist for C/C++ off of which all other language bindings are based
1676
- (openglwiki2018). Bindings exist for a wide variety of other languages such as python, C#,
1677
- and LISP and for every major operating system (openglwiki2018). The Lightweight Java Game
1678
- Library (lwjgl) is a popular Java library that provides bindings for the OpenGL API and it
1679
- will be used for example in this paper.&lt;/p&gt;
1680
- &lt;p&gt;Before OpenGL can be used, an OpenGL context (i.e. a desktop window) must be
1681
- created. Many libraries exist to do this in a cross-platform manner. One popular library
1682
- that is bundled with lwjgl is the Graphics Library Framework (GLFW), which makes it very
1683
- easy to create an OpenGL context with lwjgl. Figure 6 shows the code necessary to initialize
1684
- the GLFW framework, create an operating system window with GLFW, and then finally bind an
1685
- OpenGL context to the window for drawing. Now that a window and OpenGL context have been
1686
- created, OpenGL methods can now be called, and the window is ready for drawing.&lt;/p&gt;
1687
- &lt;p&gt;&amp;lt;figure&amp;gt;
1688
- ```java
1689
- // Initialize GLFW
1690
- glfwInit();&lt;/p&gt;
1691
- &lt;pre&gt;&lt;code&gt;// Create a new window with a given width, height, and title
1692
- long window = glfwCreateWindow(300, 300, &quot;Hello World!&quot;, NULL, NULL);
1693
-
1694
- // Set the newly created window at the current OpenGL context
1695
- glfwMakeContextCurrent(window);
1696
-
1697
- // Show the window
1698
- glfwShowWindow(window);
1699
-
1700
- // Creates OpenGL bindings using the current context
1701
- GL.createCapabilities();
1702
- ```
1703
- &lt;/code&gt;&lt;/pre&gt;
1704
- &lt;p&gt;&amp;lt;figcaption&amp;gt;Figure 6&amp;lt;/figcaption&amp;gt;
1705
- &amp;lt;/figure&amp;gt;&lt;/p&gt;
1706
- &lt;h2&gt;VBOs, VAOs, and Uniforms&lt;/h2&gt;
1707
- &lt;p&gt;OpenGL provides the standard primitives used in graphics rendering --- points,
1708
- lines, and triangles. All of these primitives are represented as float arrays which can be
1709
- passed to OpenGL for rendering. These float arrays are stored in vertex buffer objects
1710
- (VBOs). VBOs are used to allocate memory on graphics hardware and are bound along with other
1711
- objects to vertex array objects (VAOs). Creating a VBO with OpenGL is a simple task, but it
1712
- would be beneficial to understand how the OpenGL APIs work before going further into the
1713
- subject.&lt;/p&gt;
1714
- &lt;p&gt;The OpenGL API has state that is shared globally, which includes what vertex array
1715
- objects and vertex buffer objects are currently bound. Communication between the CPU and GPU
1716
- is relatively slow so it should be minimized. Minimizing communication is done by buffering
1717
- data to be drawn ahead of time and reusing the buffered data whenever possible. Due to the
1718
- limitations of the Java Virtual Machine (JVM) all memory being shared with native libraries
1719
- must be allocated off of the JVM heap (lwjglwiki). lwjgl provides utilities to easily
1720
- allocate this memory through the MemoryStack class. Data created on this stack can be sent
1721
- to the graphics hardware with lwjgl. Unsigned integers, referred to as names, are used to
1722
- uniquely identify OpenGL elements such as VBOs and VAOs. Many OpenGL functions take these
1723
- names as arguments or rely on the currently bound VBO or VAO.&lt;/p&gt;
1724
- &lt;p&gt;Figure 7 shows the code needed to store the vertices of a triangle on a graphics
1725
- card. Line two asks OpenGL to create a new buffer name and store it. Next that buffer is
1726
- bound so that all subsequent operations that require an array buffer will use it. An array
1727
- of floats representing the vertices of a triangle are created, stored in native memory, and
1728
- the finally transferred to the graphics card on line 22. A VBO is now stored on the graphics
1729
- card, but a VAO must first be created and bound before the VBO can be used.&lt;/p&gt;
1730
- &lt;p&gt;&amp;lt;figure&amp;gt;
1731
- ```java
1732
- // Create a VBO and store its name
1733
- glVboName = glGenBuffers();&lt;/p&gt;
1734
- &lt;pre&gt;&lt;code&gt;// Bind the VBO created in the last step
1735
- glBindBuffer(GL_ARRAY_BUFFER, glVboName);
1736
-
1737
- float[] vertices = new float[]{
1738
- 0.0f, 0.5f, 0.0f,
1739
- -0.5f, -0.5f, 0.0f,
1740
- 0.5f, -0.5f, 0.0f
1741
- };
1742
-
1743
- try (var stack = MemoryStack.stackPush()) {
1744
- // Allocate a native buffer to store the vertices
1745
- var vertexBuffer = stack.mallocFloat(vertices.length);
1746
-
1747
- // Put the previously declared vertices into the float buffer
1748
- vertexBuffer.put(vertices);
1749
- vertexBuffer.flip();
1750
-
1751
- // Send the vertices to the graphics hardware
1752
- glBufferData(GL_ARRAY_BUFFER, vertexBuffer, GL_STATIC_DRAW);
1753
- }
1754
- ```
1755
- &lt;/code&gt;&lt;/pre&gt;
1756
- &lt;p&gt;&amp;lt;figcaption&amp;gt;Figure 7&amp;lt;/figcaption&amp;gt;
1757
- &amp;lt;/figure&amp;gt;&lt;/p&gt;
1758
- &lt;p&gt;A vertex array object stores state so that drawing can be done quickly and is
1759
- required to be created before anything can be drawn. A VAO is bound and then set up so that
1760
- when an object needs to be drawn in the future it only needs to be rebound without any
1761
- further instruction before drawing. A vertex array simply keeps pointers to buffers which
1762
- can and should be bound to multiple VAOs to save memory on the graphics hardware. Figure 8
1763
- shows the relationship between vertex array objects and vertex buffer objects. Notice how
1764
- the VAO has what is essentially an array of pointers to buffers. OpenGL allows you to set
1765
- the pointer at each index to buffers that contain information about what is being drawn.
1766
- Vertex buffer objects often contain vertex position coordinates, but they can contain other
1767
- data used to render the element such as color and texture data.&lt;/p&gt;
1768
- &lt;p&gt;&amp;lt;figure&amp;gt;
1769
-
1770
- &amp;lt;figcaption&amp;gt;Figure 8, two VAOs with one VBO each
1771
- (devries2019).&amp;lt;/figcaption&amp;gt;
1772
- &amp;lt;/figure&amp;gt;&lt;/p&gt;
1773
- &lt;p&gt;As shown in Figure 9, creating a VAO is similar to creating a VBO. The
1774
- glGenVertexArrays method allocates space for a VAO on the graphics card and returns its
1775
- name. The name is then bound with the glBindVertexArray method so that all future methods
1776
- that require a bound VAO use the newly created VAO. The previously created VBO is rebound on
1777
- line 8 to ensure that the VAO points to the correct VBO. Line 11 sets the 0th index of the
1778
- bound VAO to the currently bound VBO object. Its other arguments define the size, type, and
1779
- layout of the data. The last step is to enable the 0th index on the VAO --- otherwise OpenGL
1780
- will not pass the buffer when rendering.&lt;/p&gt;
1781
- &lt;p&gt;&amp;lt;figure&amp;gt;
1782
- ```java
1783
- // Create a VAO and store its name
1784
- glVaoName = glGenVertexArrays();&lt;/p&gt;
1785
- &lt;pre&gt;&lt;code&gt;// Bind the VAO that was just created
1786
- glBindVertexArray(glVaoName);
1787
-
1788
- // Bind the previously created VBO
1789
- glBindBuffer(GL_ARRAY_BUFFER, glVboName);
1790
-
1791
- // Have the first VAO index point to the bound VBO
1792
- glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
1793
-
1794
- // Enable the first VAO index
1795
- glEnableVertexAttribArray(0);
1796
- ```
1797
- &lt;/code&gt;&lt;/pre&gt;
1798
- &lt;p&gt;&amp;lt;figcaption&amp;gt;Figure 9&amp;lt;/figcaption&amp;gt;
1799
- &amp;lt;/figure&amp;gt;&lt;/p&gt;
1800
- &lt;p&gt;VAOs cannot bind more than 4 elements of a VBO array, making it hard to pass large
1801
- amounts of data to shaders while rendering. Buffers also must be set per VAO which can make
1802
- code repetitive if some buffer is shared between many VAOs. Uniforms are a type of variable
1803
- which aim to solve this problem. Uniforms can store not only vectors, but also matrices.
1804
- They are intended for use where data is common between several elements. Figure 10 shows a
1805
- uniform being created to store a 4x4 projection matrix.&lt;/p&gt;
1806
- &lt;p&gt;&amp;lt;figure&amp;gt;
1807
- ```java
1808
- // Create a new perspective matrix
1809
- var matrix = new Matrix4f().perspective(FIELD_OF_VIEW, aspectRatio, Z_NEAR,
1810
- Z_FAR);&lt;/p&gt;
1811
- &lt;pre&gt;&lt;code&gt;// Get the name of the matrix
1812
- glUniformName = glGetUniformLocation(glShaderProgramName, &quot;projectionMatrix&quot;);
1813
-
1814
- // Move the matrix to native memory, and then buffer it
1815
- try (MemoryStack stack = MemoryStack.stackPush()) {
1816
- FloatBuffer fb = stack.mallocFloat(16);
1817
- matrix.get(fb);
1818
- glUniformMatrix4fv(glUniformName, false, fb);
1819
- }
1820
- ```
1821
- &lt;/code&gt;&lt;/pre&gt;
1822
- &lt;p&gt;&amp;lt;figcaption&amp;gt;Figure 10&amp;lt;/figcaption&amp;gt;
1823
- &amp;lt;/figure&amp;gt;&lt;/p&gt;
1824
- &lt;h3&gt;Indexed Rendering&lt;/h3&gt;
1825
- &lt;p&gt;Consider the case of drawing a square. This would require two right triangles of
1826
- equal dimensions, each connected at their hypotenuses. Each triangle would only have one
1827
- unique coordinate, with the other two being shared at the hypotenuses. Right now, this would
1828
- have to be sent as six coordinates as show in Figure 11, instead of only four. While these
1829
- two extra coordinates may seem insignificant, this inefficient use of graphics memory may
1830
- cause problems, especially when rendering complex scenes with hundreds or thousands of
1831
- triangles. This problem can be solved with indexed rendering.&lt;/p&gt;
1832
- &lt;p&gt;Indexing adds a layer of indirection when rendering. Rather than directly passing
1833
- the coordinates to draw to OpenGL, a list of indices is passed. OpenGL then uses this list
1834
- to access the true data in its buffers. The code to create an element buffer is very similar
1835
- to that of creating VAOs and VBOs, so it will be omitted. Instead, a comparison of the data
1836
- being passed to OpenGL will be provided.&lt;/p&gt;
1837
- &lt;p&gt;The figure below shows a comparison of the code to draw a square with and without
1838
- indexing. Notice how on Figure 12 each vertex is defined once, and then the vertices used
1839
- for each triangle is defined in a separate array. The numbers in the indices array
1840
- represents which index in the vertices array should be used when drawing. For example, the
1841
- value 0 in the indices array refers to the 0th entry in the vertices array, which is the
1842
- top-left vertex.&lt;/p&gt;
1843
- &lt;p&gt;Creating a square without indexing.&lt;/p&gt;
1844
- &lt;p&gt;&amp;lt;figure&amp;gt;
1845
- &lt;code&gt;java float[] vertices = new float[]{ // Format: x, y, z // Triangle one 0.0f,
1846
- 0.0f, 0.0f, // Top left 0.0f, -1f, 0.0f, // Bottom left 1f, 0.0f, 0.0f, // Top right //
1847
- Triangle two 0.0f, -1f, 0.0f, // Bottom left -1f, -1f, 0.0f, // Bottom right 0.0f, -1f, 0.0f
1848
- // Top right }; &lt;/code&gt;&lt;/p&gt;
1849
- &lt;p&gt;&amp;lt;figcaption&amp;gt;Figure 11, creating a square without
1850
- indexing.&amp;lt;/figcaption&amp;gt;
1851
- &amp;lt;/figure&amp;gt;&lt;/p&gt;
1852
- &lt;p&gt;&amp;lt;figure&amp;gt;
1853
- ```java
1854
- float[] vertices = new float[]{
1855
- 0.0f, 0.0f, 0.0f, // Top left
1856
- 0.0f, -1f, 0.0f, // Bottom left
1857
- 1f, 0.0f, 0.0f, // Top right
1858
- -1f, -1f, 0.0f, // Bottom right
1859
- };&lt;/p&gt;
1860
- &lt;pre&gt;&lt;code&gt;int[] indices = new int[] {
1861
- 0, 1, 2, // Triangle One
1862
- 0, 2, 3 // Triangle Two
1863
- };
1864
- ```
1865
- &lt;/code&gt;&lt;/pre&gt;
1866
- &lt;p&gt;&amp;lt;figcaption&amp;gt;Figure 12, creating a square with
1867
- indexing.&amp;lt;/figcaption&amp;gt;
1868
- &amp;lt;/figure&amp;gt;&lt;/p&gt;
1869
- &lt;h2&gt;The Rendering Pipeline&lt;/h2&gt;
1870
- &lt;p&gt;Now that a VAO and VBO have been created, OpenGL is almost ready to draw. The only
1871
- thing left to do is to tell OpenGL what to do with the data it receives from VAOs, VBOs, and
1872
- uniforms. When OpenGL receives a draw command, it runs the VAO through a pipeline in order
1873
- to determine what to draw and how to draw it. This pipeline is responsible for positioning,
1874
- rasterizing, and coloring primitives. The pipeline is divided into several stages, many of
1875
- which are configurable through programs written in the OpenGL shading language (GLSL).
1876
- Figure 13 shows a simplified view of the OpenGL pipeline and Figure 14 shows how the input
1877
- is transformed throughout the pipeline. While OpenGL allows several of its stages to be
1878
- programmed or configured by the user, some of the stages are fixed-function, meaning that
1879
- OpenGL does not allow any customization to the stage. A couple of notable fixed-function
1880
- stages include the vertex fetch stage, the primitive assembly stage, and per-sample
1881
- processing. The vertex fetch stage is the first stage in the pipeline and feeds the pipeline
1882
- with data from VAOs and VBOs. The primitive assembly stage performs an optimization known as
1883
- face culling to ensure that only visible triangles are processed. Per-sample processing is
1884
- the final stage in the OpenGL pipeline and ensures that previously drawn elements are not
1885
- overwritten unless the new element&apos;s depth is higher through a process called depth
1886
- testing and writes fragments to the framebuffer to be displayed.&lt;/p&gt;
1887
- &lt;p&gt;While there are several configurable stages in the pipeline, only two must be
1888
- defined for rendering to occur. These two stages are the vertex shader and fragment shader.
1889
- The vertex shader tells OpenGL where a primitive is located, while the fragment shader
1890
- contributes to the final color of a primitive during rendering.&lt;/p&gt;
1891
- &lt;p&gt;&amp;lt;figure&amp;gt;
1892
-
1893
- &amp;lt;figcaption&amp;gt;
1894
- Figure 13, a simplified OpenGL pipeline (sellers2016). Boxes with square corners are
1895
- programmable by the user.
1896
- &amp;lt;/figcaption&amp;gt;
1897
- &amp;lt;/figure&amp;gt;&lt;/p&gt;
1898
- &lt;p&gt;&amp;lt;figure&amp;gt;
1899
- &amp;lt;div class=&quot;bg-white&quot;&amp;gt;&amp;lt;/div&amp;gt;
1900
- &amp;lt;figcaption&amp;gt;Figure 14, visualization of data flowing through the OpenGL
1901
- pipeline (overvoorde2019).&amp;lt;/figcaption&amp;gt;
1902
- &amp;lt;/figure&amp;gt;&lt;/p&gt;
1903
- &lt;p&gt;In order to customize the OpenGL pipeline a shader program must first be created.
1904
- Once it is created, shaders can be compiled, attached to the program, and then linked. This
1905
- final executable is run on the graphics hardware and executed by OpenGL when rendering.
1906
- Figure 15 shows the code needed to create an OpenGL shader program.&lt;/p&gt;
1907
- &lt;p&gt;&amp;lt;figure&amp;gt;
1908
- ```java
1909
- // Create IDs for a shader program and vertex shader
1910
- glShaderProgramName = glCreateProgram();
1911
- glVertexShaderName = glCreateShader(GL_VERTEX_SHADER);&lt;/p&gt;
1912
- &lt;pre&gt;&lt;code&gt;// Associate the shader with a string containing its source code
1913
- glShaderSource(glVertexShaderName, vertexShaderSource);
1914
-
1915
- // Compile the stored source for the shader
1916
- glCompileShader(glVertexShaderName);
1917
-
1918
- // Associate a shader with a shader program
1919
- glAttachShader(glShaderProgramName, glVertexShaderName);
1920
-
1921
- // Create an executable from the attached shaders
1922
- glLinkProgram(glShaderProgramName);
1923
-
1924
- // Set OpenGL to use the shader program when rendering
1925
- glUseProgram(glShaderProgramName);
1926
- ```
1927
- &lt;/code&gt;&lt;/pre&gt;
1928
- &lt;p&gt;&amp;lt;figcaption&amp;gt;Figure 15&amp;lt;/figcaption&amp;gt;
1929
- &amp;lt;/figure&amp;gt;&lt;/p&gt;
1930
- &lt;h3&gt;Writing Shaders&lt;/h3&gt;
1931
- &lt;p&gt;As mentioned before writing shaders is done in the OpenGL shading language which is
1932
- based off of C. Figure 16 shows a simple vertex shader program. The first line declares the
1933
- version of OpenGL that the shader is using which is OpenGL 3.3 for this shader. Lines
1934
- starting with &quot;layout&quot; tell OpenGL what input the shader is expecting. In this
1935
- case this shader expects two inputs --- vectors of size 3 and size 4. The location indicates
1936
- which index of the VAO these vectors should be found. The 0th index was set in Figure 16 and
1937
- represents the position of the element. The creation of the 1st index was omitted for
1938
- brevity, however as the shader program shows it represents the color of the element. Lines
1939
- starting with &quot;out&quot; indicate what data this shader passes on to future stages in
1940
- the pipeline. Uniforms are global variables set. This shader contains only one uniform which
1941
- is a 4x4 projection matrix.&lt;/p&gt;
1942
- &lt;p&gt;The main function is where the work is being done. gl_Position is a special
1943
- variable that tells OpenGL the final position of an element. It is assigned to the input
1944
- position after it is multiplied by the projection matrix. This position is passed on, as
1945
- well as the color that was passed to the shader.&lt;/p&gt;
1946
- &lt;p&gt;&amp;lt;figure&amp;gt;
1947
- ```glsl
1948
- #version 330 core&lt;/p&gt;
1949
- &lt;pre&gt;&lt;code&gt;// A vector of 3 floats at index 0 of the VAO
1950
- layout (location = 0) in vec3 position;
1951
-
1952
- // A vector of 4 floats at index 1 of the VAO
1953
- layout (location = 1) in vec4 inColor;
1954
-
1955
- // Output a vector of 4 floats
1956
- out vec4 color;
1957
-
1958
- // A global 4x4 matrix
1959
- uniform mat4 projectionMatrix;
1960
-
1961
- void main() {
1962
- // Transform the position of the vertex by the projection matrix
1963
- gl_Position = projectionMatrix \* vec4(position, 1.0);
1964
-
1965
- // Pass the color to the fragment shader
1966
- color = inColor;
1967
- }
1968
- ```
1969
- &lt;/code&gt;&lt;/pre&gt;
1970
- &lt;p&gt;&amp;lt;figcaption&amp;gt;Figure 16&amp;lt;/figcaption&amp;gt;
1971
- &amp;lt;/figure&amp;gt;&lt;/p&gt;
1972
- &lt;p&gt;The fragment shader partially determines the color of pixels. Figure 17 shows a
1973
- basic fragment shader which receives a color from the vertex shader in Figure 16 and outputs
1974
- the color as-is.&lt;/p&gt;
1975
- &lt;p&gt;&amp;lt;figure&amp;gt;
1976
- ```glsl
1977
- #version 330 core&lt;/p&gt;
1978
- &lt;pre&gt;&lt;code&gt;// A vector of 4 floats in
1979
- in vec4 color;
1980
-
1981
- // A vector of 4 floats out
1982
- out vec4 outColor;
1983
-
1984
- void main() {
1985
- // Set the outgoing color to the incoming color
1986
- outColor = color;
1987
- }
1988
- ```
1989
- &lt;/code&gt;&lt;/pre&gt;
1990
- &lt;p&gt;&amp;lt;figcaption&amp;gt;Figure 17&amp;lt;/figcaption&amp;gt;
1991
- &amp;lt;/figure&amp;gt;&lt;/p&gt;
1992
- &lt;h3&gt;Textures&lt;/h3&gt;
1993
- &lt;p&gt;Textures allow images to be stored and used while rendering. While OpenGL supports
1994
- many kinds of textures, only standard 2D textures will be covered here. The process for
1995
- creating a texture follows the same pattern as VBOs. A texture name must first be created
1996
- and bound, and then data can be loaded into the texture. This code is show in Figure 18.
1997
- Once the texture is initialized, it can be used in a fragment shader.&lt;/p&gt;
1998
- &lt;p&gt;The fragment shader uses a special kind of uniform type called a sampler. Samplers
1999
- represent textures in shaders (sellers2016). The fragment shader uses a special function
2000
- called &lt;code&gt;texture&lt;/code&gt;, which takes a sampler and a coordinate. The
2001
- coordinate represents where in the texture image the fragment should get its data from. In
2002
- example 19 the shader receives the coordinate from the vertex shader.&lt;/p&gt;
2003
- &lt;p&gt;&amp;lt;figure&amp;gt;
2004
- ```java
2005
- // Create and bind a texture
2006
- glTextureName = glGenTextures();
2007
- glBindTexture(GL_TEXTURE_2D, glTextureName);&lt;/p&gt;
2008
- &lt;pre&gt;&lt;code&gt;// Load a texture into the bound texture buffer
2009
- glTexImage2D(GL_TEXTURE_2D,
2010
- 0,
2011
- GL_RGBA,
2012
- width,
2013
- height,
2014
- 0,
2015
- GL_RGBA,
2016
- GL_UNSIGNED_BYTE,
2017
- imageData);
2018
- ```
2019
- &lt;/code&gt;&lt;/pre&gt;
2020
- &lt;p&gt;&amp;lt;figcaption&amp;gt;Figure 18, creating a texture buffer and loading image
2021
- data into it.&amp;lt;/figcaption&amp;gt;
2022
- &amp;lt;/figure&amp;gt;&lt;/p&gt;
2023
- &lt;p&gt;&amp;lt;figure&amp;gt;
2024
- ```glsl
2025
- #version 330 core&lt;/p&gt;
2026
- &lt;pre&gt;&lt;code&gt;vec2 textureCoord;
2027
-
2028
- out vec4 outColor;
2029
-
2030
- // Uses the bound texture
2031
- uniform sampler2D textureSampler;
2032
-
2033
- void main() {
2034
- outColor = texture(textureSampler, textureCoord);
2035
- }
2036
- ```
2037
- &lt;/code&gt;&lt;/pre&gt;
2038
- &lt;p&gt;&amp;lt;figcaption&amp;gt;Figure 19, loading texture data in the fragment
2039
- shader.&amp;lt;/figcaption&amp;gt;
2040
- &amp;lt;/figure&amp;gt;&lt;/p&gt;
2041
- &lt;h2&gt;Conclusion&lt;/h2&gt;
2042
- &lt;p&gt;With all of these concepts a basic OpenGL program can be created. Figure 20 shows a
2043
- program (Full program source available &lt;a
2044
- href=&quot;https://github.com/ShepherdJerred-homework/seminar-application&quot;&gt;here&lt;/a&gt;)
2045
- that renders a simple textured cube that rotates. It uses all of the previously covered
2046
- material, and most of the code samples used above. While this program is simple, it composes
2047
- many concepts and lays the foundation for more complex applications. Games can be created
2048
- with only these concepts. The only further work with graphics would be to render more
2049
- elements and make the objects more complex.&lt;/p&gt;
2050
- &lt;p&gt;&amp;lt;figure&amp;gt;
2051
- &amp;lt;video controls&amp;gt;
2052
- &amp;lt;source src={cube} type=&quot;video/mp4&quot; /&amp;gt;
2053
- &amp;lt;/video&amp;gt;
2054
- &amp;lt;figcaption&amp;gt;Figure 20, a spinning textured cube.&amp;lt;/figcaption&amp;gt;
2055
- &amp;lt;/figure&amp;gt;&lt;/p&gt;
2056
- &lt;p&gt;Computer graphics are crucial to providing easy-to-use experiences for users, and
2057
- in creating modern day entertainment and other visual media. OpenGL allows you to render
2058
- graphics by writing simple shader programs and feeding the OpenGL pipeline with input data.
2059
- This data can then efficiently be rendered and displayed on-screen to users. The pipeline
2060
- can transform input and allows programmers to modify how their input is rendered. While
2061
- there is a lot of overhead in graphics rendering, it allows the user to get the most out of
2062
- their graphics hardware.&lt;/p&gt;
2063
- &lt;h2&gt;References&lt;/h2&gt;
2064
- &lt;pre&gt;&lt;code&gt;@misc{hernandez2019,
2065
- author = &quot;Antonio Hernández Bejarano&quot;,
2066
- title = &quot;Lwjglgamedev&quot;,
2067
- url = &quot;ahbejarano.gitbook.io/lwjglgamedev/&quot;,
2068
- }
2069
-
2070
- @misc{devries2019,
2071
- author = &quot;Joey de Vries&quot;,
2072
- title = &quot;Learn OpenGL&quot;,
2073
- url = &quot;learnopengl.com/&quot;
2074
- }
2075
-
2076
- @misc{galloy2013,
2077
- author = &quot;Michael Galloy&quot;,
2078
- title = &quot;CPU vs GPU Performance.&quot;,
2079
- url = &quot;michaelgalloy.com/2013/06/11/cpu-vs-gpu-performance.html&quot;
2080
- }
2081
-
2082
- @misc{larkin2016,
2083
- author = &quot;Jeff Larkin&quot;,
2084
- title = &quot;GPU Fundamentals.&quot;,
2085
- url =
2086
- &quot;www.icl.utk.edu/~luszczek/teaching/courses/fall2016/cosc462/pdf/GPU\_Fundamentals.pdf&quot;
2087
- }
2088
-
2089
- @misc{lwjgl,
2090
- author = &quot;lwjgl&quot;,
2091
- title = &quot;Lightweight Java Game Library&quot;,
2092
- url = &quot;www.lwjgl.org&quot;
2093
- }
2094
-
2095
- @misc{lwjglwiki,
2096
- author = &quot;lwjgl wiki&quot;,
2097
- title = &quot;Lwjgl Wiki&quot;,
2098
- url = &quot;github.com/LWJGL/lwjgl3-wiki/&quot;
2099
- }
2100
-
2101
- @misc{masserann2018,
2102
- author = &quot;Arnaud Masserann&quot;,
2103
- title = &quot;OpenGL Tutorial&quot;,
2104
- url = &quot;www.opengl-tutorial.org/&quot;
2105
- }
2106
-
2107
- @misc{mckesson2018,
2108
- author = &quot;Jason L. McKesson&quot;,
2109
- title = &quot;Learning Modern 3D Graphics Programming.&quot;,
2110
- url = &quot;paroj.github.io/gltut/&quot;
2111
- }
2112
-
2113
- @misc{openglwiki2018,
2114
- author = &quot;Khronos&quot;,
2115
- title = &quot;OpenGL Wiki&quot;,
2116
- url = &quot;www.khronos.org/opengl/wiki/Main\_Page&quot;
2117
- }
2118
-
2119
- @misc{overvoorde2019,
2120
- author = &quot;Alexander Overvoorde&quot;,
2121
- title = &quot;OpenGL Tutorial&quot;,
2122
- url = &quot;open.gl/&quot;
2123
- }
2124
-
2125
- @book{sellers2016,
2126
- author = &quot;Graham Sellers, et al.&quot;,
2127
- title = &quot;OpenGL Superbible: Comprehensive Tutorial and Reference&quot;,
2128
- year = &quot;2016&quot;,
2129
- publisher = &quot;Addison-Wesley&quot;
2130
- }
2131
- &lt;/code&gt;&lt;/pre&gt;
2132
- </content:encoded>
2133
- </item>
2134
- </channel>
2135
- </rss>