2,384 karma · joined August 29, 2011
data:text/html,<body%20bgcolor="white">Regardless, even if hypothetical product X could do everything cURL and libcurl do for most people, that wouldn't make cURL/libcurl much less load-bearing, any more than the existence of FreeBSD or Illumos make Linux less load-bearing.
What does this even mean? cURL is one of the most load-bearing pieces of software in existence. It, and the Linux kernel, which takes a similarly dim view of the CVE system, are the inventors. "Not Invented Here" seems to imply that there is a vast body of peer work for them to draw on to resolve this problem, but who are their peers? As far as I can tell, the answer is something like "Microsoft and Apple", on the one hand, who exist in a totally different, mostly closed-source or at least closed-development, ecosystem, or something like "glibc and OpenSSL" on the other hand, which have their own storied CVE history.
All repos need to end up using SHA-2 exclusively by the end. All tools that speak only SHA-1 need to be made incompatible intentionally. If the SHA-1/SHA-2 hybrid approach could allow that to happen, then it would be useful. If not, then it would just be a waste of time.
Yes, actually. The price of 1 pint of real vanilla extract is currently $9.59 at Costco. I have seen it as high as $42.99. That is a nearly 5x spread, and it largely depends on the weather and politics in Madagascar.
The point is that Java does not have this problem in the language or the standard library. Of course, you should not write racy Go code; the language provides ample ways to avoid the race, such as channels; and the race detector will generally find such racy code, provided you turn it on. However, the issue is that this race leads to memory-safety violations; it can occur especially in code written by novice Go programmers, and it's well acknowledged by the language authors [1].
Go slices are fat pointers to undecorated memory. The slice itself is a 3-tuple of pointer, length, and capacity. If you append to a slice that's already at capacity, the Go runtime will allocate new memory for you and return a new 3-tuple. If you assign that result to a variable that's also being accessed by another goroutine, the latter can observe the slice in an inconsistent state. It can, for example, see the old pointer but with the new length, allowing out-of-bounds access. None of this requires unsafe code.
The same issue applies to string and interface variables, which are also fat pointers.
Bethesda is not a very good counter-example, in my opinion, because they regularly** break mod support. I don't know enough about how these mods work to be sure, but given that the 32-bit to 64-bit transition was one such major breaking event, I am suspicious that they are not actually effectively sandboxed. I also don't play many Paradox games (and the one I did play a lot of, Cities Skylines, is a special case, and also a Unity/C# game), so I don't know how they do things.
The best example I know of for strong, sandboxed, first-party mod support would be Factorio and its Lua mods. However, these mods are constrained to doing only those things the game developers anticipate and want to allow, which means every mod for Factorio produces a very Factorio-flavored experience, though AFAIK there are some extension points in the API which are only really used by mods (or were, at least, before the Space Age expansion).
The biggest downside of C# (and Java) mods is indeed security, but the biggest upside is that a lot of games can add modding support without too much extra work. Such extra work is often not possible (they don't own the engine) or unlikely to happen (they don't have the time/budget), outside of special cases.
* = Whatever else you might think of Nexus Mods and its policies and drama, they generally take malware seriously
** = Roughly every 3-4 years, which means it's happened like 4 times with Skyrim, and one of those was so major that it completely split the mod ecosystem
Most modern (post-2000) languages actually handle this perfectly fine. They have both signed and unsigned types, with exact bit widths and fully specified semantics, which generally match what the hardware is natively capable of, at least on modern processors.
It's languages from the 1990s that make this complicated. There must have been something in the water that motivated language designers in that decade to "simplify" the number system. Maybe this was an overcorrection from even older languages, which generally weren't trying to be clever but were trying to be portable, at a time when a lot of the basics hadn't been nailed down yet, like 8-bit bytes and two's complement.
The primary way to deal with error-on-clean-up in RAII languages is to not rely exclusively on RAII for it. Rust's File type, for example, has sync_data and sync_all methods (which, to be fair, only even need to be called for writable file handles). I don't think there's anything wrong with this approach, but it ends up being just as explicit and therefore forgettable as defer.
It should be noted that you can (at least in Rust) actually implement defer using RAII; see e.g. the scopeguard crate. Since RAII is block-scoped, this defer is also block-scoped (like Zig) rather than function-scoped (like Go).
Marker interfaces can exist in Go, but here they are another kind of hack. They must define at least one method (which doesn't have to be public), though that method is never actually meant to be called.
The real hack to me is that anything which simply has Lock() and Unlock() methods is considered uncopyable.
cpu: AMD Ryzen 5 5600X 6-Core Processor
BenchmarkAES_CBC-12 100000000 10.96 ns/op
BenchmarkAES_CTR-12 83161234 14.36 ns/op
BenchmarkPCG-12 345336063 3.463 ns/op
BenchmarkChaCha8-12 174143492 6.894 ns/op
BenchmarkXoshiro256p-12 254343658 4.717 ns/op
BenchmarkXoshiro256pp-12 266837442 4.496 ns/op
vs. cpu: Apple M4 Pro
BenchmarkAES_CBC-14 162698020 7.370 ns/op
BenchmarkAES_CTR-14 242501074 4.954 ns/op
BenchmarkPCG-14 197000988 6.083 ns/op
BenchmarkChaCha8-14 237430095 5.050 ns/op
BenchmarkXoshiro256p-14 252911710 4.738 ns/op
BenchmarkXoshiro256pp-14 252656401 4.745 ns/op
Code: https://gist.github.com/kbolino/afbb86f3c9b2bd2f87272801d156...* = Including urllib.parse (Python), url_parse (PHP), java.net.URI (Java), System.Uri (.NET), net/url.URL (Go), curl_url_get (libcurl), URL (JavaScript, which calls it the "hash"), url::Url (Rust), Boost.URL (C++)
Moreover, the "obvious" solution, i.e. treating (T,error) returns the same as Result<T,E>, does not actually work. The problem is that (T,error) behaves like a product type while Result<T,E> is a sum type and yes, some Go code does (ab)use this. In particular, the io.Reader.Read method in the standard library allows implementers to return (n>0,io.EOF), which has no equivalent Result representation. Many people consider this allowance to be a mistake, but it's too late to change it.