netzstaub

beatz & funkz

Thursday, February 24, 2005

Papierkurzfassung: Maintaining High Bandwidth…

Wie versprochen, eine kleine Zusammenfassung des Papiers “Maintaining High Bandwidth under Dynamic Network Conditions”, das ich gestern kurz verlinkt hatte. Thema des Artikels ist das Verteilen von Daten in einem Peer-to-Peer Netzwerk, zum Beispiel um ISOs oder Patches zu verteilen, oder Multimediadaten zu verteilen, ohne einen hohen Load auf den “primären” Datenverteiler einbüssen zu müssen. Die Daten, die verteilt werden, sind meistens nur von einer einzigen Quelle erhältlich, werden aber von vielen Leuten gleichzeitig und schnell gezogen (in dem Artikel, und scheinbar auch woanders, wird dieser Effekt “flash-crowd effect” genannt).

Seit geraumer Zeit gibt es jetzt zum Beispiel BitTorrent, in dem Artikel wird untersucht, wie man ein solches Verfahren verbessern kann. Das Interessante ist aber, dass bei dem Versuch diese Verteilungsverfahren zu optimieren, verschiedene Alternativen vorgeschlagen und dokumentiert werden. Man kriegt also einen Einblick, wie solche Peer-to-Peer Protokolle zu designen sind, je nachdem, welchen Schwerpunkt man haben will. Eine wichtige Entscheidung ist z.B., ob man ein faires Netzwerk haben will, wo praktisch jeder Knoten gleich runterlädt und hochlädt, oder ein schnelles Netzwerk (bei dem also ein Knoten so schnell zu den Daten kommt, die er haben will). Die untersuchten Entscheidungen in dem Artikel sind:

  1. Ob Knoten (bzw. Peers) in dem P2P-Netz die Daten gesendet bekommen (PUSH-Modell), oder diese Daten anfragen (PULL-Modell)
  2. Welcher Algorithmus eingesetzt wird, um neue Knoten zu finden, die gute “Transfer”-Charakteristiken haben (Bandbreite, Latenz)
  3. Welche Daten angefordert werden sollen, je nachdem wie die Netzwerksituation ist
  4. Welche Daten anderen Peers zu senden sind
  5. Welche Verfahren eingesetzt werden, um die Daten umzukodieren (z.B. Fehlerkorrektur, oder gar keine Kodierung)

Push oder Pull?



Push ist natürlich die Methode, die einfacher zu realisieren ist. Bei Streaming-anwendungen sollten z.B. alle empfangenden Knoten dieselben Daten mit derselben Geschwindigkeit bekommen. Allerdings wird es gleich komplizierter, wenn Daten von mehreren Sendern gleichzeitig empfangen werden. In dem Fall müssen die Empfänger den Sendern mitteilen, welche Daten sie schon empfangen, damit nicht doppelte Pakete verschickt werden (alles klar?). Bei Pull können die Empfänger genauer kontrollieren, was sie empfangen wollen und von wem. Allerdings wird dadurch die Latenz der Kommunikation erhöht, und es kann unter Umständen auch zu einer nicht zu ignorierenden Erhöhung der ausgetauschten Datenmenge kommen. Hier gibt es also keinen klaren Gewinner, und in dem implementierten Ansatz der Autoren werden auch beide Verfahren gleichzeitig verwendet.

Nachbarnsuche (Peering-Strategie)

Ein extrem wichtiger Punkt (vielleicht der wichtigste) ist, wie ein Knoten seine Nachbarn findet, wie es sie auswählt, und wie er neue Nachbarn hinzuzieht, wenn Knoten ausfallen oder sich ihre Verbindung verschlechtert. Im Idealfall weiss jeder Knoten über alle anderen Knoten bescheid, und kennt auch ihre Parameter in Sachen Bandbreite, usw… Das ist natürlich kaum möglich, denn der Overhead, diese ganze Daten im Netzwerk zu verteilen wäre viel zu hoch (das ist auch so eins der Probleme der herkömmlichen Multicast-Routing Algorithmen). Eine andere Möglichkeit wäre, das ein zentraler Knoten diese Information festhält, und ich glaube (aber ich weiss nicht wirklich genau wie Bittorrent funktionniert), dass in Bittorrent diese Rolle von dem Tracker übernommen wird. Ist dieser Tracker nun weg oder schlecht erreichbar, bricht das Ganze zusammen. Nicht zu übersehen ist auch die Bandbreite, die der Tracker dann zur Verfügung stellen muss, um diese Daten managen zu können. Ein weitere “triviale” Möglichkeit wäre einfach, dass jeder Knoten eine feste Liste von Nachbarn ermittelt, und diese dann nicht mehr ändert. Wenn diese Knoten aufhören oder nicht mehr erreichbar sind, oder diese Anfangsliste einfach schlecht ist, hat der Knoten verloren. Ein sinnvoller Ansatz ist also eine Peeringstrategie, wo der Knoten nicht über ein globales Wissen verfügen muss (das gesamte Netzwerk kennen), und auch nicht mit vielen Nachbarknoten kommunizieren muss, aber trotzdem relativ schnell gute Nachbarn finden kann.

Ich weiss nicht, wie die das im Artikel lösen (noch nicht komplett durchgelesen), aber ein sinnvoller Ansatz wäre denke ich eine Art “randomisierte” Aggregierung von Nachbarsdaten durch die Knoten selber. Also Knoten A kommuniziert mit 10 Knoten, welche selber 10 Knoten als Nachbarn haben, A kann mit diesen 10 Knoten die kompletten Nachbarsdaten austauschen (also mit Tiefe 1), irgendwie die 5 Besten + 5 Zufällige zusammenbasteln, und an weitere Knoten schicken (oder an den Tracker, der deswegen nicht alle Daten von allen Peers kennen muss). Kommt ein neuer Knoten in das Netz, kriegt er eine zufällige, aggregierte Menge von Nachbarn, und kann sich so durchhangeln. Gehen ein paar Knoten hops, können ihre Nachbarn beim Tracker wieder “randomisierte”, im Durchschnitt gute Nachbarn bekommen. Hauptsache, im Durchschnitt klappt es gut, und relativ schnell kann man die Nachbarnauswahl verfeinern, indem man neue Nachbarn hinzunimmt, und die schlechtesten rauswirft.

So, das ist auch genug für heute, denn langsam wird es spät. Mehr in den nächsten Tagen.

posted by manuel at 12:28 am  

Wednesday, February 23, 2005

Drucken und drucken…

Dieser Eintrag wird ein bisschen nach Anfaenger-Eintrag aussehen, aber was solls. Ich habe beschlossen all die Dinger, mit denen ich rumgefrustet habe, zu bloggen, dann bleibt wenigstens eine Spur fuer mich uebrig. Und dieses “Ach den Schrott hab ich doch schonmal gemacht, wie ging das denn nur”-Gefuehl ist dann hoffentlich weg. Ein ganz christlicher Hintergedanke dabei ist auch, dass ich dadurch anderen Leuten vielleicht helfen kann. Dann wird mein Karma besser, ich kann ohne Alptraueme schlafen, und der ganzen guten Welt geht es dann praechtig.

Also, erster Eintrag in dieser Kategorie: heute habe ich beschlossen meinen alten guten lauten langsamen DecLaser 3500 zu ersetzen, und zwar durch einen billig-Tintenpisser. Der DecLaser kann nicht mal Dokumente korrekt ausdrucken, die mit Pages erzeugt worden sind. Auf jeder Seite ist dann so eine Art haesslicher grauer Hintergrund zu sehen, der total nach Apple-hype aussehend Brief sieht dann nur noch nach Guelle aus, und sowieso hat der Drucker keine Duplex-Einheit. Die hat naemlich TNT auf dem Schrott entsorgt, *seufz*. Da ist er also, der neue Drucker, ein CANON PIXMA iP3000 (weil die Patronen so billig sind, “dass sich sogar Refillen nicht mehr lohnt”). Der kann naemlich auch CDs bedrucken, was sehr wichtig ist (wie will man sonst professionell aussehende Software-releases unters Volk bringen?). Also, Drucker ausgepackt, erstmal 10 Minuten nach dem Stromanschluss gesucht (das nicht im Handbuch dokumentiert ist): wer es auch sucht, links hinten :) Dann den Drucker in den Airport Express gesteckt, Patronen eingesteckt, und dann gings los mit der Scheisssoftware. Ich habe 2 Clientrechner fuer den Drucker, spaeter wird Dividuum noch das Teil mit CUPS benutzen: eine OSX Kiste, und eine WinXP Kiste. Angefangen mit der XP Kiste, Treiber von der CD installiert, dann neuen Drucker hinzufuegen ueber die XP Oberflaeche. Da ist es ganz wichtig, als PORT einen TCP/IP PORT zu definieren mit der IP des Airport Expresses, und den “RAW”-Modus auszuwaehlen (bei mir war LPR angewaehlt, und das mit dem RAW habe ich dann erst 2 Stunden spaeter rausgekriegt). Danach als Druckertyp einfach Canon PIXMA iP3000 auswaehlen, Printer Heads konfigurieren, und alles muesste funktionnieren. Ganz wichtig ist wie gesagt dieser “RAW” Modus, port 9100, SNMP hab ich ausgeklickt.

Unter OSX war das noch einfacher. CD rein, Treiber installiert, neustarten (na super), dann Print irgendwo auswaehlen, und automagisch taucht da der iP3000 in der Liste auf (als Rendezvous printer). Das wars. OSX ist in dem Punkt schon ganz nett :)

posted by manuel at 9:46 pm  

Wednesday, February 23, 2005

Da gibt es nette Musik

Scofield

Und zwar hier, ich habe bis jetzt nur ein paar Tracks von MMW with John Scofield (was auch immer MMW ist). Das Dir kam durch Schrikdraad.

posted by manuel at 9:11 pm  

Wednesday, February 23, 2005

Und ecto spinnt auch rum

Irgendwie spinnt die Zeit in ecto rum, deswegen die komische Reihenfolge der Beiträge. Ob das wohl mit meinem LD_PRELOAD zusammenhängt? :}

posted by manuel at 12:58 am  

Tuesday, February 22, 2005

Streamingpapier

Ein sehr interessantes Paper zum Thema Contentdistribution über P2P Netzwerke ist Maintaining High Bandwidth under Dynamic Network Conditions, von Dejan Kostic, Ryan Braud, Charles Killian, Erik Vandekieft, James W. Anderson, Alex C. Snoeren und Amin Vahdat. Morgen mehr zu dem Paper.

posted by manuel at 11:54 pm  

Tuesday, February 22, 2005

Der geile bl0rgo-caster

Ich lebe in der wilden bl0rg-wg, und die muss ganz im bl0rg-stil auch komplett automatisiert, überwacht und verkabelt werden. Zum Beispiel müssen die Einkäufe automatisch erfasst werden, damit der Traum des selbstfüllenden Kühlschranks ein bisschen näher rückt. Das Tolle ist: ziemlich jedes Produkt, das man kaufen kann, ist mit einem Barcode versehen (bis auf das Brot vom Bäcker, aber das kriegen wir auch noch irgendwie geregelt). Im Entropia lag noch ein alter Barcodescanner, der prompt analysiert wurde, und wie sich rausstellte ganz bequem die Barcodes über seriell rausgebeamt hat. Nach ein bisschen Rumfrusten mit bestimmten seriellen Interfaces, die nicht funktionierten, kann man jetzt die Barcodes ganz “bequem” (*hüstel*) mit folgendem Shellscript extrahieren:

#!/bin/sh
DEV=/dev/ttyE0
stty -F $DEV cs8 -parenb -cstopb speed 9600 icrnl -ixon
while true; do
    i=`head -n 1 $DEV`
    echo -en "barcode $i\nquit\n" | nc localhost 12345
done

DEV ist hier die serielle Schnittstelle. Als erstes setzt das Skript die nötigen Parameter für die Kommunikation (9600 bauds, 8n2, Carriage Return unix-konform nach Newline konvertieren, und Flowcontrol aus). Dann wird jeweils die neuste Zeile gelesen (also das neuste Barcode), und dieser Barcode wird dem Bl0rgo-caster übergeben. Insgesamt also schön einfach, nur ein bisschen Glue notwendig. Hiermit hätten wir schonmal die Sprache Shell benutzt (und das sind auch die einzigen Zeilen code von mir in diesem geilen System, der Rest ist alles von Dividuum. Ich hab mich da gleich zu Anfang irgendwie mit meinem UDP Broadcast-system verfahren, und konnte nicht mal sinnvoll UDP-Nachrichten auf dem Ethernet schicken).

Der bl0rgo-caster an sich besteht aus einem Server, der über UDP Broadcast messages senden und empfangen kann, und diese Nachrichten über TCP an verschiedene Listener dann übermitteln kann. Also, wenn man einfach nur Nachrichten broadcasten will, kann man eine UDP Nachrichten schicken. Wenn man sie empfangen will, kann man über TCP connecten, und lauschen. Der Clou ist aber, dass der bl0rgo-caster über LUA skriptbar ist. Wenn man also per TCP connected, kriegt man eigentlich eine LUA-Shell, und kann dort LUA-Kommandos absetzen. Beim Start parst der Server eine Skript-datei, die er dann auch später reloaden kann. Dort kann man dann z.B. Kommandos einbauen, um XMMS zu steuern. Der bl0rgo-caster benutzt libevent, um die verschiedenen Datenquellen zu pollen. Der LUA-Code, der z.B. unsere XMMS-Barcodes analysiert, sieht wie folgt aus:

function onBroadcast(datatype, data)
    -- print("onUDPData: " .. datatype .. " - " .. data)
    -- print(datatype)
    -- print(data)
    -- print("---")
    if datatype == 0 then
        Client.writelnAll("Message: " .. data)
    elseif datatype == 1 then
        Client.writelnAll("Barcode scanned: " .. data)
        if (data == "1234567") then
            os.execute("xmms -r")
        elseif (data == "1231313") then
            os.execute("xmms -f")
        else
            print("huh?")
        end
    end
end

Hier sieht man, wie der Messagetyp analysiert wird, und wenn ein Barcode kam XMMS gesteuert wird. Doch das war für den wilden Dividuum nicht genug, ein weiteres Tool in der bl0rgo-caster Suite zeigt Broadcast messages als X11-Popup an, so dass man immer am Laufenden gehalten wird, was gerade in der WG passiert (denn nachher werden alle Sensoren in der WG ihre Nachrichten in den bl0rgo-caster dumpen). Diese Popuptool ist in Ruby geschrieben, und verwendet intern TK um die Grafik anzuzeigen. Das sind also insgesamt 5 Sprachen, die zum Einsatz kommen: ruby, C, shell, Lua und Tk (insofern Tk als Sprache gilt). Beim Testen von Popup unter meiner Debian kam noch ein komischer Fehler vor: die clear Methode von TkText war irgendwie nicht definiert (?), also mussten wir wirklich ein Stückchen Tk reinziehen:

# UAAAAAAAAAAAAAAArgggggg
class TkText
 def clear
  tk_send_without_enc('delete', "1.0", 'end')
  self
 end
end

Wie lecker… :)

Den Code für den bl0rgo-caster könnt ihr aus unserem Subversion ziehen:

$ svn co svn://bl0rg.net/bl0rgo-caster/

Erweiterungsideen sind z.B. ein Listener, der alle Events als RSS veröffentlicht. Mal sehen, das werden wir bestimmt in Lisp oder so hacken, damit der Sprachenchaos komplett ist.

posted by manuel at 11:51 pm  

Tuesday, February 22, 2005

Zeit faken unter Unix

Weil jemand im IRC danach gesucht hat, hier ein kleines Stück Code um die Zeit für ein Programm zurückzusetzen. Ziemlich alle Unixoide Programme werden wohl den Systemcall gettimeofday benutzen, um die Uhrzeit zu bekommen. Zum Glück ruft man syscalls nicht von Hand auf, denn es gibt ganz viele schöne Wrapperfunktionen in der libc. In dem Fall die gut benannte Funktion gettimeofday. Nun wird die libc meistens dynamisch verlinkt, so dass man einen weit verbreiteten Trick anwenden kann, um System Calls zu hijacken. Der dynamische Loader kann nämlich Libraries “vorschalten”. Im Klartext heisst das also: wir tun gar nicht Syscalls hijacken, sondern die Wrapperfunktionen zu diesen Syscalls. Und weil wir selber so faul sind, wollen wir aus unserer Hijackfunktion nicht noch selber den richtigen Syscall aufrufen, sondern rufen gleich die eigentlich Funktion auf. Im Code sieht das dann so aus:

#include <stdlib.h>
#include <sys/time.h>
#include <dlfcn.h>
#include <assert.h>

typedef int (*gettimeofday_t)(struct timeval *, struct timezone *);

static gettimeofday_t old_gettimeofday = NULL;

#ifndef RTLD_NEXT
/* linux... */
#define RTLD_NEXT (-1l)
#endif

int gettimeofday(struct timeval *tp, struct timezone *tzp) {
  int ret;

  if (old_gettimeofday == NULL) {
    old_gettimeofday = (gettimeofday_t)dlsym(RTLD_NEXT, "gettimeofday");
    assert(old_gettimeofday != NULL);
  }

  ret = (*old_gettimeofday)(tp, tzp);
  if (tp) {
    tp->tv_sec -= 50000;
  }

  return ret;
}

Als erstes checken wir ob wir schon die Adresse von der “richtigen” Funktion kennen, und wenn nicht, dann holen wir die uns mit der Funktion dlsym. An der Stelle ein kleiner übler Hack für Linux. Da muss man irgendwie __USE_GNU definieren, damit der sich die gewollte Definition von dlsym holt, nach 2 Versuchen ging das bei mir irgendwie nicht, ich hatte nicht weiter bock, deswegen dieses #define RTLD_NEXT. Nachdem wir die alte Funktion haben, rufen wir diese auf, ziehen 5000 Sekunden von dem Ergebnis ab, et voila. Das Ganze kann mit folgendem Makefile compilen:

all: libfakedate.so

libfakedate.so: fakedate.c
    $(CC) -D__USE_GNU -fPIC -shared -o $@ $

Und mit folgender Zeile dann benutzen:

$ LD_PRELOAD=`pwd`/libfakedate.so date

Schick! Unter OSX tut das nicht, sondern loopt, aus welchen undefinierbaren Gründen auch immer (kein ELF, sondern Mach-O, portiertes dlcompat, ach-weiss-ich…).

posted by manuel at 10:54 pm  

Tuesday, February 22, 2005

Lisp nach Javascript

Wegen unkontrollierbarem, auswucherndem Idlen heute nur ein kleiner Einblick in die Konvertierung von Lisp nach Javascript.

Klammeraffe

Ein nettes Feature bei Lisp sind die Klammern. Ja, die Klammern, diese kleinen Rattenschwänze die die meisten Leute zuerst abturnen, wenn sie Lisp-Code sehen. “Ieeh, das ist ja unlesbar, wie soll man sich da zurechtfinden?”. Das dachte auch John McCarthy bei der Entwicklung von Lisp, und hatte eigentlich fest eingeplant, da später mal eine “normale” Infixe Syntax drüber zu kippen. Doch wichtiger war zuerst überhaupt eine funktionnierende Implementierung, und die benutzte intern wie praktisch jede andere Programmiersprache einen abstrakten Syntaxbaum (AST - Abstract Syntax Tree), das man auf einem “linearen” Medium wie Text einfach durch Postfixausdrücke niederschreiben kann. Bei Lisp waren das die SEXPs (S-Expression, ich glaube das S steht für Syntax). Doch diese SEXPs erwiesen sich als ungemein praktisch, weil sie eigentlich selber Listen sind. Und die Liste ist der primitive Datentyp von Lisp (LISP heisst ja auch nichts anderes als LISt Processing). Das heisst, das man in Lisp ohne grosse Hürden Lisp-Code manipulieren kann. Klingt nach wilden Voodo-Opfer-Parties im dunklen Bayou, und ich will auch nicht verschweigen, dass es manchmal auch so ist. Aber in der Praxis heisst das dann meistens folgendes:

  • Fehlt ein Konstrukt in der Sprache, lässt es sich einfach herbeizaubern
  • Will man Lisp-Code oder sowas ähnliches transformieren, braucht man keinen Parser und Lexer und Compiler zu schreiben, sondern man wirft einfach ein paar Lisp-Funktionen und noch 2-3 Makros zum abrunden und fertig ist die Software.

Wie das genau funktionniert lässt sich ganz schön anhand des folgenden Beispiels zeigen. Angenommen, man hat eine Webplattform, die in Lisp geschrieben ist. Die Plattform generiert dynamisch Webseiten, die aus HTML bestehen. In den meisten Programmiersprachen macht man das mit einem Wust von “print”-Statements, in LISP schreibt man 1 Makro und brauch nur noch Listen zu generieren. Kleiner Einblick:

Dieses Macro definiert die Spezialform, um ein CMS-internen Link zu erzeugen.

(defmacro cmslink (url &body body)
  `(html ((:a :class "cmslink" :href ,url)
          ,@body)))

Wenn man jetzt aber Javascript noch einführen will, dann wird das ganz schön anstrengend, denn so einfach geformt wie HTML ist javascript nun wieder nicht. Aber auf ganz viele “print”, bzw FORMAT Aufrufe zurückzugreifen, ne, wirklich, das gehört sich nicht. Und den Javascript-Code aufzutrennen in zusätzliche Dateien ist je nachdem auch ein bisschen zuviel Aufwand. Und was ist, wenn Javascript-Code dynamisch generiert werden soll, z.B. um Inhalte aus der Datenbank “effizient” in Javascript zu mappen (anstatt jetzt wilde XML/RPC Orgien in javascript zu veranstalten). Nun, ein komplizierte Sprache ist Javascript nicht wirklich, man könnte doch sowas ähnliches wie Linj bauen. Linj ist eine Art “Compiler”, der eine Lisp-ähnliche Sprache nach lesbarem Java konvertiert. Lisp-ähnlich heisst hier eher sowas wie ein “Subset” von Lisp, was nach Java “umgewandelt” wird. Also nix mit Compilen nach Java-Bytecode, sondern Java-Quellcode, und Quellcode den man noch lesen kann. Der Ansatz ist hier praktisch alle Java-Konstrukte in Lisp nachzumodellieren, bzw. die Lisp-Konstrukte wie DO, DEFUN, usw… nach Java umzuwandeln.

Der erste Schritt ist hier die Variablennennung, die bei Javascript vielleicht noch wichtiger ist, weil Javascript meistens sehr sehr viel auf die schon existierenden Element im DOM-Baum zurückgreift. Problem ist, Lisp (oder zumindest der Lisp-Reader) ist case-insensitive. Wenn ich also foObAr oder FOOBAR oder fooBar oder foobar schreibe, der Lisp-Reader macht daraus immer FOOBAR. Wenn ich jetzt wirklich mit camelCase was machen will, dann muss ich mir da echt Mühe geben, und z.B. |camelCase| schreiben, also überall so kleine hässliche Pipes reinbauen. Das ist auch keinem Entwickler (oder Entwicklerin) zumutbar, da muss was anderes her. Da hab ich mir was ganz ungeniert bei Linj abgeschaut. Bei Linj wird die LISP-übliche Variablen und Funktionsnennung nach Java konvertiert, bzw. andersrum: getFoobar in Java wird da zu get-foobar in Lisp. Die Konstante +MAX-SIZE+ in Lisp wird zu MAXSIZE in Java, WTF?#! in Lisp wird zu WTFQuestionHashBang in Java. Um das zu machen bedarf es ein bisschen Magie, die ich hier mal *hust* erläutern werde. Ach ja, der Code hier ist nicht wirklich fein, nicht wirklich bugfrei, nicht wirklich getestet, aber ich wollte ja heute noch unbedingt etwas posten:

(defvar *special-chars*
  '((#\! . "Bang")
    (#\? . "What")
    (#\# . "Hash")
    (#\@ . "At")
    (#\% . "Percent")
    (#\+ . "Plus")))

(defun string-chars (string)
  (coerce string 'list))

(defun constant-string-p (string)
  (let ((len (length string))
        (constant-chars '(#\+ #\*)))
    (and (> len 2)
         (member (char string 0) constant-chars)
         (member (char string (1- len)) constant-chars))))

(defun symbol-to-js (symbol)
  (when (symbolp symbol)
    (setf symbol (symbol-name symbol)))
  (let (res
        (lowercase t)
        (all-uppercase nil))
    (when (constant-string-p symbol)
      (setf all-uppercase t
            symbol (subseq symbol 1 (1- (length symbol)))))
    (flet ((reschar (c)
             (push (if (and lowercase (not all-uppercase))
                       (char-downcase c)
                       (char-upcase c)) res)
             (setf lowercase t)))
      (dotimes (i (length symbol))
        (let ((c (char symbol i)))
          (case c
            (#\- (setf lowercase (not lowercase)))
            ('(#\! #\? #\+ #\$ #\@ #\# #\%)
             (dolist (i (coerce (cdr (assoc c *special-chars*)) 'list))
               (reschar i)))
            (t (reschar c))))))
    (coerce (nreverse res) 'string)))

Als erstes haben wir hier eine Assoziationsliste namens *SPECIAL-CHARS*, die die Zuweisung von “schrägen” Buchstaben in Lispnotation zu ihrem verbosen Bloatäquivalent in Javascript festhält. Eine Assoziationsliste ist so etwas wie ein “Poor Man’s Hash-Table”, in Lisp gibt es da eine Funktion ASSOC mit der man da einfach drinsuchen kann. Weiter geht’s mit der Funktion STRING-CHARS, die ein String zu einer Listen von Buchstaben konvertiert, eigentlich nur da weil mich das Rumgehampel mit COERCE genervt hat, es gibt da aber bestimmt bessere Wege, über ein String zu iterieren. Die Funktion CONSTANT-STRING-P guckt eigentlich nur nach, ob der vorliegende String in Lispnotation sowas wie eine “Lisp-Konstante” ist, also sowas wie +KONSTANTE+ oder *KONSTANTE*. Das -P am Ende der Funktion steht für “Predicate” und heisst, dass CONSTANT-STRING-P irgendwas auf Wahrheit überprüft (in dem Fall ob der String eine Konstante nennt), und T (für Wahr) oder NIL (für Falsch) zurückgibt. So, und SYMBOL-TO-JS ist das Prachtstück, und ich bin gerade zu müde, um diesen Bloatcode zu erklären, gehe deshalb jetzt schlafen, und mache morgen weiter. In der Zeit dürft ihr ja untersuchen, was da genau passiert. Wer ineffiziente Grütze findet darf sie ja gerne behalten, oder vielleicht ein bisschen in die Kommentare unten schmieren (die jetzt wieder gehen müssten, und vor Blinden und Spammern effektiv geschützt sind durch MT Secure Code).

posted by manuel at 12:16 am  

Monday, February 21, 2005

Radio Chaotica - Leben am Existenzmaximum

Julian hat hier die neue Radio Chaotica folge announced:

Radio Chaotica - Leben am Existenzmaximum.

Zum Ziehen auf dem elitären bl0rg-tracker.

posted by manuel at 5:51 pm  

Monday, February 21, 2005

Chevelle

Chevelle

Chevelle ist eine Pop/Schrammel Band, erinnert leicht an Weezer, lässt sich hören. Ich hab erfahren, dass es bei Opendir anscheinend einen Link zu ein paar Alben von denen gab. Auf einer seltsamen georgischen Seite. Samples von allen Liedern kann man im iTunes Store oder auf ihrer Webseite hören (leider alles flash).

(Herm, Weezer schreibt man Weezer, jaja).

posted by manuel at 11:34 am  
« Previous PageNext Page »

Powered by WordPress