• Stars
    star
    2,296
  • Rank 20,053 (Top 0.4 %)
  • Language
    C++
  • License
    Other
  • Created about 12 years ago
  • Updated about 3 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Lightweight Web Worker API implementation with native threads

WebWorker Threads

Build Status

This is based on @xk (jorgechamorro)'s Threads A GoGo for Node.js, but with an API conforming to the Web Worker standard.

This module provides an asynchronous, evented and/or continuation passing style API for moving blocking/longish CPU-bound tasks out of Node's event loop to JavaScript threads that run in parallel in the background and that use all the available CPU cores automatically; all from within a single Node process.

Note: If you would like to require() native modules in a worker, please consider using the process-based tiny-worker instead. It does not use threads, but the WebWorker API is compatible.

This module requires Node.js 0.10.0+ and a working node-gyp toolchain.

Illustrated Writeup

There is an illustrated writeup for the original use case of this module:

Event Threaded Server (multi-core)

Installing the module

With npm:

npm install webworker-threads

Sample usage (adapted from MDN):

var Worker = require('webworker-threads').Worker;
// var w = new Worker('worker.js'); // Standard API

// You may also pass in a function:
var worker = new Worker(function(){
  postMessage("I'm working before postMessage('ali').");
  this.onmessage = function(event) {
    postMessage('Hi ' + event.data);
    self.close();
  };
});
worker.onmessage = function(event) {
  console.log("Worker said : " + event.data);
};
worker.postMessage('ali');

A more involved example in LiveScript syntax, with five threads:

{ Worker } = require \webworker-threads

for til 5 => (new Worker ->
  fibo = (n) -> if n > 1 then fibo(n - 1) + fibo(n - 2) else 1
  @onmessage = ({ data }) -> postMessage fibo data
)
  ..onmessage = ({ data }) ->
    console.log "[#{ @thread.id }] #data"
    @postMessage Math.ceil Math.random! * 30
  ..postMessage Math.ceil Math.random! * 30

do spin = -> setImmediate spin

Introduction

After the initialization phase of a Node program, whose purpose is to setup listeners and callbacks to be executed in response to events, the next phase, the proper execution of the program, is orchestrated by the event loop whose duty is to juggle events, listeners and callbacks quickly and without any hiccups nor interruptions that would ruin its performance.

Both the event loop and said listeners and callbacks run sequentially in a single thread of execution, Node's main thread. If any of them ever blocks, nothing else will happen for the duration of the block: no more events will be handled, no more callbacks nor listeners nor timeouts nor setImmediate()ed functions will have the chance to run and do their job, because they won't be called by the blocked event loop, and the program will turn sluggish at best, or appear to be frozen and dead at worst.

What is WebWorker-Threads

webworker-threads provides an asynchronous API for CPU-bound tasks that's missing in Node.js:

var Worker = require('webworker-threads').Worker;
require('http').createServer(function (req,res) {
  var fibo = new Worker(function() {
    function fibo (n) {
      return n > 1 ? fibo(n - 1) + fibo(n - 2) : 1;
    }
    this.onmessage = function (event) {
      postMessage(fibo(event.data));
    }
  });
  fibo.onmessage = function (event) {
    res.end('fib(40) = ' + event.data);
  };
  fibo.postMessage(40);
}).listen(port);

And it won't block the event loop because for each request, the fibo worker will run in parallel in a separate background thread.

API

Module API

var Threads= require('webworker-threads');
.Worker

new Threads.Worker( [ file | function ] ) returns a Worker object.

.create()

Threads.create( /* no arguments */ ) returns a thread object.

.createPool( numThreads )

Threads.createPool( numberOfThreads ) returns a threadPool object.


Web Worker API

var worker= new Threads.Worker('worker.js');
var worker= new Threads.Worker(function(){ ... });
var worker= new Threads.Worker();
.postMessage( data )

worker.postMessage({ x: 1, y: 2 }) sends a data structure into the worker. The worker can receive it using the onmessage handler.

.onmessage

worker.onmessage = function (event) { console.log(event.data) }; receives data from the worker's postMessage calls.

.terminate()

worker.terminate() terminates the worker thread.

.addEventListener( type, cb )

worker.addEventListener('message', callback) is equivalent to setting worker.onmesssage = callback.

.dispatchEvent( event )

Currently unimplemented.

.removeEventListener( type )

Currently unimplemented.

.thread

Returns the underlying thread object; see the next section for details. Note that this attribute is implementation-specific, and not part of W3C Web Worker API.


Thread API

var thread= Threads.create();
.id

thread.id is a sequential thread serial number.

.load( absolutePath [, cb] )

thread.load( absolutePath [, cb] ) reads the file at absolutePath and thread.eval(fileContents, cb).

.eval( program [, cb])

thread.eval( program [, cb]) converts program.toString() and eval()s it in the thread's global context, and (if provided) returns the completion value to cb(err, completionValue).

.on( eventType, listener )

thread.on( eventType, listener ) registers the listener listener(data) for any events of eventType that the thread thread may emit.

.once( eventType, listener )

thread.once( eventType, listener ) is like thread.on(), but the listener will only be called once.

.removeAllListeners( [eventType] )

thread.removeAllListeners( [eventType] ) deletes all listeners for all eventTypes. If eventType is provided, deletes all listeners only for the event type eventType.

.emit( eventType, eventData [, eventData ... ] )

thread.emit( eventType, eventData [, eventData ... ] ) emits an event of eventType with eventData inside the thread thread. All its arguments are .toString()ed.

.destroy( /* no arguments */ )

thread.destroy( /* no arguments */ ) destroys the thread.


Thread pool API

threadPool= Threads.createPool( numberOfThreads );
.load( absolutePath [, cb] )

threadPool.load( absolutePath [, cb] ) runs thread.load( absolutePath [, cb] ) in all the pool's threads.

.any.eval( program, cb )

threadPool.any.eval( program, cb ) is like thread.eval(), but in any of the pool's threads.

.any.emit( eventType, eventData [, eventData ... ] )

threadPool.any.emit( eventType, eventData [, eventData ... ] ) is like thread.emit(), but in any of the pool's threads.

.all.eval( program, cb )

threadPool.all.eval( program, cb ) is like thread.eval(), but in all the pool's threads.

.all.emit( eventType, eventData [, eventData ... ] )

threadPool.all.emit( eventType, eventData [, eventData ... ] ) is like thread.emit(), but in all the pool's threads.

.on( eventType, listener )

threadPool.on( eventType, listener ) is like thread.on(), but in all of the pool's threads.

.totalThreads()

threadPool.totalThreads() returns the number of threads in this pool: as supplied in .createPool( number )

.idleThreads()

threadPool.idleThreads() returns the number of threads in this pool that are currently idle (sleeping)

.pendingJobs()

threadPool.pendingJobs() returns the number of jobs pending.

.destroy( [ rudely ] )

threadPool.destroy( [ rudely ] ) waits until pendingJobs() is zero and then destroys the pool. If rudely is truthy, then it doesn't wait for pendingJobs === 0.


Global Web Worker API

Inside every Worker instance from webworker-threads, there's a global self object with these properties:

.postMessage( data )

postMessage({ x: 1, y: 2 }) sends a data structure back to the main thread.

.onmessage

onmessage = function (event) { ... } receives data from the main thread's .postMessage calls.

.close()

close() stops the current thread.

.addEventListener( type, cb )

addEventListener('message', callback) is equivalent to setting self.onmesssage = callback.

.dispatchEvent( event )

dispatchEvent({ type: 'message', data: data }) is the same as self.postMessage(data).

.removeEventListener( type )

Currently unimplemented.

.importScripts( file [, file...] )

importScripts('a.js', 'b.js') loads one or more files from the disk and eval() them in the worker's instance scope.

.thread

The underlying thread object; see the next section for details. Note that this attribute is implementation-specific, and not part of W3C Web Worker API.


Global Thread API

Inside every thread .create()d by webworker-threads, there's a global thread object with these properties:

.id

thread.id is the serial number of this thread

.on( eventType, listener )

thread.on( eventType, listener ) is just like thread.on() above.

.once( eventType, listener )

thread.once( eventType, listener ) is just like thread.once() above.

.emit( eventType, eventData [, eventData ... ] )

thread.emit( eventType, eventData [, eventData ... ] ) is just like thread.emit() above.

.removeAllListeners( [eventType] )

thread.removeAllListeners( [eventType] ) is just like thread.removeAllListeners() above.

.nextTick( function )

thread.nextTick( function ) is like process.nextTick(), but much faster.


Global Helper API

Inside every thread .create()d by webworker-threads, there are some helpers:

console.log(arg1 [, arg2 ...])

Same as console.log on the main process.

console.error(arg1 [, arg2 ...])

Same as console.log, except it prints to stderr.

puts(arg1 [, arg2 ...])

puts(arg1 [, arg2 ...]) converts .toString()s and prints its arguments to stdout.

Developer guide

See the developer guide if you want to contribute.


WIP WIP WIP

Note that everything below this line is under construction and subject to change.

Examples

A.- Here's a program that makes Node's event loop spin freely and as fast as possible: it simply prints a dot to the console in each turn:

cat examples/quickIntro_loop.js
(function spinForever () {
  setImmediate(spinForever);
})();

B.- Here's another program that adds to the one above a fibonacci(35) call in each turn, a CPU-bound task that takes quite a while to complete and that blocks the event loop making it spin slowly and clumsily. The point is simply to show that you can't put a job like that in the event loop because Node will stop performing properly when its event loop can't spin fast and freely due to a callback/listener/setImmediate()ed function that's blocking.

cat examples/quickIntro_blocking.js
function fibo (n) {
  return n > 1 ? fibo(n - 1) + fibo(n - 2) : 1;
}

(function fiboLoop () {
  process.stdout.write(fibo(35).toString());
  setImmediate(fiboLoop);
})();

(function spinForever () {
  setImmediate(spinForever);
})();

C.- The program below uses webworker-threads to run the fibonacci(35) calls in a background thread, so Node's event loop isn't blocked at all and can spin freely again at full speed:

cat examples/quickIntro_oneThread.js
function fibo (n) {
  return n > 1 ? fibo(n - 1) + fibo(n - 2) : 1;
}

function cb (err, data) {
  process.stdout.write(data);
  this.eval('fibo(35)', cb);
}

var thread= require('webworker-threads').create();

thread.eval(fibo).eval('fibo(35)', cb);

(function spinForever () {
  process.stdout.write(".");
  setImmediate(spinForever);
})();

D.- This example is almost identical to the one above, only that it creates 5 threads instead of one, each running a fibonacci(35) in parallel and in parallel too with Node's event loop that keeps spinning happily at full speed in its own thread:

cat examples/quickIntro_fiveThreads.js
function fibo (n) {
  return n > 1 ? fibo(n - 1) + fibo(n - 2) : 1;
}

function cb (err, data) {
  process.stdout.write(" ["+ this.id+ "]"+ data);
  this.eval('fibo(35)', cb);
}

var Threads= require('webworker-threads');

Threads.create().eval(fibo).eval('fibo(35)', cb);
Threads.create().eval(fibo).eval('fibo(35)', cb);
Threads.create().eval(fibo).eval('fibo(35)', cb);
Threads.create().eval(fibo).eval('fibo(35)', cb);
Threads.create().eval(fibo).eval('fibo(35)', cb);

(function spinForever () {
  setImmediate(spinForever);
})();

E.- The next one asks webworker-threads to create a pool of 10 background threads, instead of creating them manually one by one:

cat examples/multiThread.js
function fibo (n) {
  return n > 1 ? fibo(n - 1) + fibo(n - 2) : 1;
}

var numThreads= 10;
var threadPool= require('webworker-threads').createPool(numThreads).all.eval(fibo);

threadPool.all.eval('fibo(35)', function cb (err, data) {
  process.stdout.write(" ["+ this.id+ "]"+ data);
  this.eval('fibo(35)', cb);
});

(function spinForever () {
  setImmediate(spinForever);
})();

F.- This is a demo of the webworker-threads eventEmitter API, using one thread:

cat examples/quickIntro_oneThreadEvented.js
var thread= require('webworker-threads').create();
thread.load(__dirname + '/quickIntro_evented_childThreadCode.js');

/*
  This is the code that's .load()ed into the child/background thread:
  
  function fibo (n) {
    return n > 1 ? fibo(n - 1) + fibo(n - 2) : 1;
  }

  thread.on('giveMeTheFibo', function onGiveMeTheFibo (data) {
    this.emit('theFiboIs', fibo(+data)); //Emits 'theFiboIs' in the parent/main thread.
  });
  
*/

//Emit 'giveMeTheFibo' in the child/background thread.
thread.emit('giveMeTheFibo', 35);

//Listener for the 'theFiboIs' events emitted by the child/background thread.
thread.on('theFiboIs', function cb (data) {
  process.stdout.write(data);
  this.emit('giveMeTheFibo', 35);
});

(function spinForever () {
  setImmediate(spinForever);
})();

G.- This is a demo of the webworker-threads eventEmitter API, using a pool of threads:

cat examples/quickIntro_multiThreadEvented.js
var numThreads= 10;
var threadPool= require('webworker-threads').createPool(numThreads);
threadPool.load(__dirname + '/quickIntro_evented_childThreadCode.js');

/*
  This is the code that's .load()ed into the child/background threads:
  
  function fibo (n) {
    return n > 1 ? fibo(n - 1) + fibo(n - 2) : 1;
  }

  thread.on('giveMeTheFibo', function onGiveMeTheFibo (data) {
    this.emit('theFiboIs', fibo(+data)); //Emits 'theFiboIs' in the parent/main thread.
  });
  
*/

//Emit 'giveMeTheFibo' in all the child/background threads.
threadPool.all.emit('giveMeTheFibo', 35);

//Listener for the 'theFiboIs' events emitted by the child/background threads.
threadPool.on('theFiboIs', function cb (data) {
  process.stdout.write(" ["+ this.id+ "]"+ data);
  this.emit('giveMeTheFibo', 35);
});

(function spinForever () {
  setImmediate(spinForever);
})();

More examples

The examples directory contains a few more examples:

  • ex01_basic: Running a simple function in a thread.
  • ex02_events: Sending events from a worker thread.
  • ex03_ping_pong: Sending events both ways between the main thread and a worker thread.
  • ex04_main: Loading the worker code from a file.
  • ex05_pool: Using the thread pool.
  • ex06_jason: Passing complex objects to threads.

Rationale

Node.js is the most awesome, cute and super-sexy piece of free, open source software.

Its event loop can spin as fast and smooth as a turbo, and roughly speaking, the faster it spins, the more power it delivers. That's why @ryah took great care to ensure that no -possibly slow- I/O operations could ever block it: a pool of background threads (thanks to Marc Lehmann's libeio library) handle any blocking I/O calls in the background, in parallel.

In Node it's verboten to write a server like this:

http.createServer(function (req,res) {
  res.end( fs.readFileSync(path) );
}).listen(port);

Because synchronous I/O calls block the turbo, and without proper boost, Node.js begins to stutter and behaves clumsily. To avoid it there's the asynchronous version of .readFile(), in continuation passing style, that takes a callback:

fs.readfile(path, function cb (err, data) { /* ... */ });

It's cool, we love it (*), and there's hundreds of ad hoc built-in functions like this in Node to help us deal with almost any variety of possibly slow, blocking I/O.

But what's with longish, CPU-bound tasks?

How do you avoid blocking the event loop, when the task at hand isn't I/O bound, and lasts more than a few fractions of a millisecond?

http.createServer(function cb (req,res) {
  res.end( fibonacci(40) );
}).listen(port);

You simply can't, because there's no way... well, there wasn't before webworker-threads.

Why Threads

Threads (kernel threads) are very interesting creatures. They provide:

1.- Parallelism: All the threads run in parallel. On a single core processor, the CPU is switched rapidly back and forth among the threads providing the illusion that the threads are running in parallel, albeit on a slower CPU than the real one. With 10 compute-bound threads in a process, the threads would appear to be running in parallel, each one on a CPU with 1/10th the speed of the real CPU. On a multi-core processor, threads are truly running in parallel, and get time-sliced when the number of threads exceed the number of cores. So with 12 compute bound threads on a quad-core processor each thread will appear to run at 1/3rd of the nominal core speed.

2.- Fairness: No thread is more important than another, cores and CPU slices are fairly distributed among threads by the OS scheduler.

3.- Threads fully exploit all the available CPU resources in your system. On a loaded system running many tasks in many threads, the more cores there are, the faster the threads will complete. Automatically.

Why not multiple processes.

The "can't block the event loop" problem is inherent to Node's evented model. No matter how many Node processes you have running as a Node-cluster, it won't solve its issues with CPU-bound tasks.

Launch a cluster of N Nodes running the example B (quickIntro_blocking.js) above, and all you'll get is N -instead of one- Nodes with their event loops blocked and showing a sluggish performance.

More Repositories

1

ethercalc

Node.js port of Multi-user SocialCalc
JavaScript
2,964
star
2

lingua-sinica-perlyuyan

Perl in Classical Chinese in Perl
Perl
276
star
3

regex-genex

Given a list of regexes, generate all possible strings that matches all of them.
Haskell
145
star
4

archive.tw

archive.tw
HTML
40
star
5

Pugs.hs

Perl6 User's Golfing System in Haskell
C
35
star
6

interpolatedstring-perl6

QuasiQuoter for Perl6-style multi-line interpolated strings with q, qq and qc support.
Haskell
25
star
7

as3gif

AS3GIF lets you play and encode animated GIF's with ActionScript 3.
ActionScript
24
star
8

network-multicast

[Haskell] The "Network.Multicast" module is for sending UDP datagrams over multicast (class D) addresses.
Haskell
22
star
9

moe

่Œๅ…ธๅญ—ๅฎ™ๅๆ‡‰็ˆ โ€” Hanzi Fusion Reactor
JavaScript
20
star
10

pugs

A Perl 6 Implementation
Perl
20
star
11

wikiwyg-js

Wikiwyg is a WYSIWYG browser editor framework for wikis. It is designed to be pluggable on top of existing wiki engines, with little or no change to the wiki engine's core software.
JavaScript
19
star
12

node-sikuli-api

Sikuli standalone API binding for Node.js
JavaScript
17
star
13

democracy3-taiwan-mod

Democracy 3 Taiwan 2016 mod
16
star
14

module-signature

Module signature file manipulation
Perl
16
star
15

quickcheck-regex

Generate regex-constrained strings for QuickCheck
Haskell
15
star
16

node-cluster-server

Simple multi-CPU cluster server manager for Node 0.6+
JavaScript
15
star
17

jscex-jquery

Async/Await support for jQuery 1.5+
JavaScript
14
star
18

encode-hanconvert

[Perl] Modules and scripts for Traditional and Simplified Chinese mappings
Perl
11
star
19

app-uni

Command-line utility to grep UnicodeData.txt
Perl
10
star
20

openafp

[Haskell] IBM AFP document format parser and generator
Haskell
10
star
21

plack-middleware-sizelimit

Terminate processes if they grow too large
Perl
10
star
22

wikicalc

Dan Bricklin's wikiCalc
Perl
10
star
23

string-qq

QuasiQuoter for non-interpolated, overloaded strings
Haskell
9
star
24

wordnet

Python
9
star
25

livehousein2slack

Pipe Livehouse.in chatroom message into Slack channel
Perl
9
star
26

Unicode-EastAsianWidth

Release history of Unicode-EastAsianWidth
Perl
8
star
27

pdf2line

[Haskell] Simple command-line utility to convert PDF into text
C++
8
star
28

selfvars

Provide $self, @args and %opts variables for OO programs
Perl
7
star
29

i18n-pm

i18n - Perl Internationalization Pragma
Perl
7
star
30

pad2an

Converts Hackpad-compatible HTML to Akoma Ntoso
HTML
6
star
31

pgre.st

PgREST landing page
CSS
6
star
32

devel-hints

Access compile-time hints at runtime
C
6
star
33

openafp-utils

Assorted utilities to work with AFP data streams
Haskell
6
star
34

regex-pcre-builtin

The PCRE backend to accompany regex-base, with bundled code from www.pcre.org
C
6
star
35

atom-language-gcode

G-Code Grammar for Atom.io
CoffeeScript
6
star
36

YiJing

Release history of YiJing
Perl
5
star
37

ethercalc-log

Log viewer and restoration tool for EtherCalc
CSS
5
star
38

polis-tally

Tally Pol.is export format
Perl
5
star
39

methods

Provide method syntax and autoclean namespaces
Perl
5
star
40

letris-solver

Simple Letris Solver
JavaScript
5
star
41

findbin

[Haskell] Locate the directory of original program
Haskell
5
star
42

snapper

Micro web application framework built on Haskell's Snap framework
Haskell
5
star
43

use

Import several modules with a single use statement
Perl
5
star
44

hssyck

[Haskell] Fast, lightweight YAML loader and dumper
C
5
star
45

namepass

Random password generator based on memorizable Chinese names
Perl
5
star
46

parse-afp

Parser for IBM's Advanced Function Printing document format
Perl
4
star
47

ournet-bbs

Component Object Model for BBS systems
Perl
4
star
48

edugame

A chinese card game invented by Bestian Tang
Haskell
4
star
49

q-jscex

Async/Await syntax support for Q promises
JavaScript
4
star
50

Test-use-ok

Release history of Test-use-ok
Perl
4
star
51

image-jpegtran-auto

Losslessly fix JPEG orientation
C
4
star
52

moosex-types-buf

Moose type definitions for Byte buffers
Perl
4
star
53

line2pdf

[Haskell] Simple command-line utility to convert text into PDF
4
star
54

ethercalc.net

http://ethercalc.net/
HTML
4
star
55

socialtext-open

Mirror of the "master" branch from Socialtext Open repository.
Perl
4
star
56

node-uax11

Convert between full/half-width ASCII characters
JavaScript
4
star
57

pdf2md

PDF to Markdown
LiveScript
3
star
58

connect-csv

Connect middleware for accepting text/csv data type
JavaScript
3
star
59

stringtable-atom

[Haskell] Memoize Strings as Atoms for fast comparison and sorting, with maps and sets
Haskell
3
star
60

MoeDict.hs

Haskell Utilities working with MoeDict.tw JSON dataset
Haskell
3
star
61

po-tools

PO file tools in Haskell
Haskell
3
star
62

module-package-au

Reusable Module::Package bits
Perl
3
star
63

batbone

Batman-esque framework built on Backbone
CoffeeScript
3
star
64

tie-discovery

[Perl] Lazily evaluated "discovery" hashes
Perl
3
star
65

pegex-coffee

Pegex.pm port to CoffeeScript
CoffeeScript
3
star
66

moosex-types-unistr

Moose type definitions for Unicode strings
Perl
3
star
67

PDF-FromHTML

Release history of PDF-FromHTML
Perl
3
star
68

sheetnode

Read-only mirror of https://drupal.org/project/sheetnode
PHP
3
star
69

process-sizelimit-core

Re-packaging Apache::SizeLimit::Core into its own distribution
Perl
3
star
70

gcin-voice-data

GCIN Mandarin Pronounciation Data from Edward Der-Hua Liu
JavaScript
2
star
71

quickcheck-relaxng

Generate RelaxNG-constrained XML documents for QuickCheck
Haskell
2
star
72

tw.ethercalc.net

http://tw.ethercalc.net/
HTML
2
star
73

slash-ournet

Web Interface for OurNet::BBS
Perl
2
star
74

acme-hello

Print a greeting message
Perl
2
star
75

Term-ANSIScreen

Release history of Term-ANSIScreen
Perl
2
star
76

convert-eastasianwidth

Convert between full- and half-width characters
Perl
2
star
77

uc_option_image

Drupal 7 port of Ubercart's "Option Image" plugin
PHP
2
star
78

Lith-Summer2012-Scripts

็ซ‹ๆ€ 2012 ๅคๅคฉๆ„›ๆ€่€ƒ็ณปๅˆ—่ฌ›ๅบง็›ธ้—œ็จ‹ๅผ
LiveScript
2
star
79

youtubevideog

Youtube video player gadget
2
star
80

tlearn

Drupal Module for online learning
PHP
1
star
81

emsg

1
star
82

PDF-Template-PDFAPI2

Release history of PDF-Template-PDFAPI2
Perl
1
star
83

Lingua-EN-Inflect-Number

Release history of Lingua-EN-Inflect-Number
Perl
1
star
84

flickr-igoogle

Display images from any Flickr account on iGoogle. (Fork from chinsonyeh's google code project under GPLv3.)
1
star
85

ttf2gis

Convert TrueType font to PostGIS table
Perl
1
star
86

react-odp-json

Convert ODP to JSON for react-odp
LiveScript
1
star
87

isism.net

Restored backup of upsaid blog posts
HTML
1
star
88

tapx-harness-socialtext

ST extended TAP::Harness
Perl
1
star
89

TimePiece

A simple tile-based digital clock screen saver written in Haskell + SDL
Haskell
1
star
90

Text-LevenshteinXS

An XS implementation of the Levenshtein edit distance
Perl
1
star
91

template-extract

Use TT2 syntax to extract data from documents
1
star
92

TPElineService

่‡บๅŒ—ๅธ‚ๆ”ฟๅบœlineๆœๅ‹™
PHP
1
star
93

locale-maketext-fuzzy

Maketext from already interpolated strings
Perl
1
star
94

DateTime-Functions

Release history of DateTime-Functions
Perl
1
star
95

flickrigoogle

Display images from any Flickr account on iGoogle. (Fork from chi
1
star
96

Win32-Symlink

Release history of Win32-Symlink
C
1
star
97

mandaring

Ring of Mandarin
JavaScript
1
star
98

lith-ddblock

Dynamic display block
PHP
1
star
99

smallcheck-regex

Generate regex-constrained strings for SmallCheck
Haskell
1
star
100

convert-geekcode

Convert and generate geek code sequences
Perl
1
star