HNHacker News
TopNewBestAskShowJobs

wasmperson

230 karma · joined July 4, 2025

submissionscomments
wasmperson··on Element: setHTML() method
I interpreted your question as "do I now no longer need to escape user-generated data in the HTML sent by the server in response to requests by HTMX?" The short answer is no, you still need to escape it:

- HTMX adds extra significance to HTML attributes which aren't accounted for by the built-in sanitizer

- HTMX can't add a custom sanitizer because it wouldn't be able to distinguish between intentional and malicious uses of those attributes

- Even if the HTMX client library sanitized all of the HTML from the server, you can't guarantee that all requests to the server will come from HTMX: browsers can navigate to your "back-end" URLs directly. While you can protect yourself from this using HTTP headers, that's not something I'd feel comfortable relying on since it would be easy to not notice when you've accidentally gotten it wrong.

The HTMX website has a longer explainer on how to protect yourself from XSS when using the library:

https://htmx.org/essays/web-security-basics-with-htmx/

wasmperson··on Arenas in Rust
> There are several ways to solve this problem. One way is to avoid using direct references to the particular class of objects at all. Instead, allocate a big array of objects, and refer to them with integer indexes into that array. There are several names that have been used for such arrays and indexes; let's call them arenas and handles.

I thought the term "Arena" referred to linear allocators, but maybe it's not so narrowly defined.

> At one level, this question is not very fair, because an answer to it as stated would be that one simply does not use doubly linked lists. They have been popular in introductory computer science lectures because they are a neat way to explain pointers in a data structure that's easy to draw on a whiteboard, but they are not a good match to modern hardware. The last time I used one was in the nineties. I know the Linux kernel uses them, but that design was also laid down in the nineties; if you were designing a kernel from scratch today, you would probably not do it that way.

The arena data structure you described is inefficient unless it uses a linked list to track empty slots. All general-purpose heap allocators use linked lists in their implementations. Linked lists show up wherever you want pointer stability, low fragmentation, or a way to decouple the storage of objects from their participation in data structures. I struggle to imagine how it would be possible to implement something like Linux without at least one linked list in it.

> Essentially you are bypassing the notion of pointers provided directly by the hardware

Pointers aren't a hardware concept, they're a language concept. Array indices and pointers both rely on indirect addressing, which is the underlying hardware concept. The "handles" strategy in Rust feels like a kludgy approach not because it bypasses the hardware but because Rust's borrow checker isn't actually involved in ensuring safety in that case, just its bounds checking and mandatory initialization (and if all you need is those two things then...).

wasmperson··on Debcraft – Easiest way to modify and build Debian packages
Yup, dpkg-deb is all you need, but I could never find it looking through online documentation. The thing that finally helped me figure out how to make .deb files was an internet outage, believe it or not. While bored I started looking at packaging-related documentation in my computer's locally-installed manual pages, and landed on these two, which explain the basics in simple terms:

https://manpage.me/index.cgi?apropos=0&q=dpkg-deb&sektion=0&...

https://manpage.me/index.cgi?apropos=0&q=deb-control&sektion...

wasmperson··on The Big Oops: Anatomy of a Thirty-Five-Year Mistake [video]
A custom engine is never _needed_ technically speaking since COTS engines are often customizable to the point where you can do anything you want with them. That doesn't mean that they don't influence the design of games, though.

There's a talk that Casey gives where he explains how he implemented the movement system for The Witness, in which he shows examples of Unity-based "walking simulator"-type games dealing with limitations of the engine in ways that The Witness was able to totally avoid. This allowed the game's artists to be creative with set design without worrying as much about performance issues or collision bugs, thus potentially opening up more design space.

https://www.youtube.com/watch?v=YE8MVNMzpbo

Here's some praise of The Witness by Fabien Giesen:

> That’s where I am right now. I have never seen another game as cohesive as this. Not even close. Without getting into specifics or spoiler territory, I have never seen a game with such manifest intention behind every single detail, nor one where all the details cohere this well into a whole. This goes for the design and gameplay itself, but also across traditional hard boundaries. Game design and art play off each other, and both of these are tightly coupled with some of the engine tech in the game. It’s amazing. There is no single detail in the game that would be hard to accomplish by itself, but the whole is much more than the sum of its parts, with no signs of the endless compromise that are normally a reality in every project.

https://fgiesen.wordpress.com/2016/01/30/thoughts-on-the-wit...

wasmperson··on C++: Zero-cost static initialization
To be fair to your coworkers, coding a windows service and setting up logging for it is surprisingly complicated. I'm the only person at my place of work that can do it, and even then only if I can use a compile-to-native language or .NET.
wasmperson··on Zig's New Writer
I'm not usually in the "defending C++" camp, but when I see this:

  pub const File = struct {
  
    pub fn writer(self: *File, buffer: []u8) Writer{
      return .{
        .file = self,
        .interface = std.Io.Writer{
          .buffer = buffer,
          .vtable = .{.drain = Writer.drain},
        }
      };
    }
  
    pub const Writer = struct {
      file: *File,
      interface: std.Io.Writer,
      // this has a bunch of other fields
  
      fn drain(io_w: *Writer, data: []const []const u8, splat: usize) !usize {
        const self: *Writer = @fieldParentPtr("interface", io_w);
        // ....
      }
    }
  }
...I can't help but think of this:

  struct FileWriter: public Writer {
      File *file;
      // this has a bunch of other fields
  
      FileWriter(File *self, span<char> buffer)
          : Writer(buffer), file(self) {}
  
      size_t drain(span<span<char const> const> data, size_t splat) override {
        // ....
      }
  };
Writing code to build a vtable and having it implicitly run at compile time is pretty neat, though!
wasmperson··on Asynchrony is not concurrency
> The main benefit of having async (or Go-style M:N scheduling) is that you can afford to launch as many tasks/fibers/goroutines/... as you want

Some have argued that the real solution to this problem is to "just" fix OS threads. Rumor has it Google has done exactly this, but keeps it close to their chest:

https://www.youtube.com/watch?v=KXuZi9aeGTw

https://lwn.net/Articles/879398/

Somewhat related and also by Google is WebAssembly Promise Integration, which converts blocking code into non-blocking code without requiring language support:

https://v8.dev/blog/jspi

I see a possible future where the "async/await" idea simply fades away outside of niche use-cases.

wasmperson··on Chrome's SSL Bypass Cheatcode
An example for anyone who hasn't seen this before:

https://subdomain.preloaded-hsts.badssl.com/

wasmperson··on Rust and WASM for Form Validation
> Obviously, the sample code above unwraps to high heaven, and that’s nothing something I would condone in actual production code—please do use proper error handling.

Everywhere the author used `unwrap` is a place where I would expect the program to crash if the operation fails, so I'm not sure what they imagine "proper error handling" in this case would look like. Take this snippet for example:

  let doc = window().unwrap().document().unwrap();
  let form = doc
      .get_element_by_id("login")
      .unwrap()
      .dyn_into::<HtmlFormElement>()
      .unwrap();
In javascript that looks like this:

  // or you could write nothing.  `login` is already a global variable
  let form = document.getElementById('login');
At a glance, the web-sys docs don't say, but I assume the error conditions that would trigger those `unwrap`s are:

- The `window` global is missing or the code is running outside of the browser

- The `document` global is missing

- The page has no form element with an id of "login"

I don't see a reasonable thing to do in those cases except crash.

A more general point: I find WebAssembly works best when:

- Interfacing with the DOM and web APIs is still mostly done in javascript

- The wasm binary has a narrow interface consisting of a handful of functions with careful calling conventions

- The wasm binary avoids dependencies on either third-party packages or the standard library (e.g. rust's "no_std")

- The compiled code generously uses mutable "global" variables (note: local to the wasm module instance)

The rust + wasm-bindgen + web-sys strategy feels like the exact opposite of this, which doesn't strike me as very useful unless you just want to avoid writing javascript entirely.

← PreviousPage 4 of 4