[thrift] preliminary Erlang support (initial import)

Summary:
 * missing {list,map,set}s, inheritance is spotty
 * loose source code, plus everything is one process (application / gen_server behavior is forthcoming)
 * codegen is a mess, need t_fp_generator

Test Plan:
 * codegen invoked without -erl generates identical code for test/
 * calculatorHandler plus 'thrift -erl -r tutorial.thrift' more or less works

Revert Plan: ok


git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@665146 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lib/erl/src-loose/protocol/tBinaryProtocol.erl b/lib/erl/src-loose/protocol/tBinaryProtocol.erl
new file mode 100644
index 0000000..b82af73
--- /dev/null
+++ b/lib/erl/src-loose/protocol/tBinaryProtocol.erl
@@ -0,0 +1,232 @@
+-module(tBinaryProtocol).
+
+-include("thrift/thrift.hrl").
+-include("thrift/protocol/tBinaryProtocol.hrl").
+-include("thrift/protocol/tProtocolException.hrl").
+
+-export([
+  new/1,
+  trans/1,
+  skip/2,
+
+  writeMessageBegin/4, writeMessageEnd/1,
+  writeStructBegin/2, writeStructEnd/1,
+  writeFieldBegin/4, writeFieldEnd/1, writeFieldStop/1,
+  writeMapBegin/4, writeMapEnd/1,
+  writeListBegin/3, writeListEnd/1,
+  writeSetBegin/3, writeSetEnd/1,
+
+  writeBool/2, writeByte/2, writeI16/2, writeI32/2, 
+  writeI64/2, writeDouble/2, writeString/2, 
+
+  readMessageBegin/1, readMessageEnd/1, 
+  readStructBegin/1, readStructEnd/1, 
+  readFieldBegin/1, readFieldEnd/1,
+  readMapBegin/1, readMapEnd/1,
+  readListBegin/1, readListEnd/1,
+  readSetBegin/1, readSetEnd/1,
+
+  readBool/1, readByte/1, readI16/1, readI32/1, 
+  readI64/1, readDouble/1, readString/1
+]).
+
+new(Trans) ->
+    #tBinaryProtocol{trans=Trans}.
+
+trans(This) -> % accessor
+    ?ATTR(trans).
+
+skip(This, Type) ->
+    tProtocol:skip(This, Type).
+
+writeMessageBegin(This, Name, Type, Seqid) ->
+    writeI32(This, ?VERSION_1 bor Type),
+    writeString(This, Name),
+    writeI32(This, Seqid).
+
+writeMessageEnd(This) ->
+    This, % suppress unused warnings
+    ok.
+
+writeStructBegin(This, Name) ->
+    This, Name, % suppress unused warnings
+    ok.
+
+writeStructEnd(This) ->
+    This, % suppress unused warnings
+    ok.
+
+writeFieldBegin(This, Name, Type, Id) ->
+    Name,
+    writeByte(This, Type),
+    writeI16(This, Id).
+
+writeFieldEnd(This) ->
+    This, % suppress unused warnings
+    ok.
+
+writeFieldStop(This) ->
+    writeByte(This, ?tType_STOP).
+
+writeMapBegin(This, Ktype, Vtype, Size) ->
+    writeByte(This, Ktype),
+    writeByte(This, Vtype),
+    writeI32(This, Size).
+
+writeMapEnd(This) ->
+    This, % suppress unused warnings
+    ok.
+
+writeListBegin(This, Etype, Size) ->
+    writeByte(This, Etype),
+    writeI32(This, Size).
+
+writeListEnd(This) ->
+    This, % suppress unused warnings
+    ok.
+
+writeSetBegin(This, Etype, Size) ->
+    writeByte(This, Etype),
+    writeI32(This, Size).
+
+writeSetEnd(This) ->
+    This, % suppress unused warnings
+    ok.
+
+%
+
+writeBool(This, Bool) ->
+    if Bool -> % true
+	    writeByte(This, 1);
+       true -> % false
+	    writeByte(This, 0)
+    end.
+
+writeByte(This, Byte) ->
+    Trans = This#tBinaryProtocol.trans,
+    ?M1(Trans, write, binary_to_list(<<Byte:8/big>>)).
+
+writeI16(This, I16) ->
+    Trans = This#tBinaryProtocol.trans,
+    ?M1(Trans, write, binary_to_list(<<I16:16/big>>)).
+
+writeI32(This, I32) ->
+    Trans = This#tBinaryProtocol.trans,
+    ?M1(Trans, write, binary_to_list(<<I32:32/big>>)).
+
+writeI64(This, I64) ->
+    Trans = This#tBinaryProtocol.trans,
+    ?M1(Trans, write, binary_to_list(<<I64:64/big>>)).
+
+writeDouble(This,  Double) ->
+    Trans = This#tBinaryProtocol.trans,
+    ?M1(Trans, write, binary_to_list(<<Double:64/big>>)).
+
+writeString(This, Str) ->
+    Trans = This#tBinaryProtocol.trans,
+    writeI32(This, length(Str)),
+    ?M1(Trans, write, Str).
+
+%
+
+readMessageBegin(This) ->
+    Version = readI32(This),
+    if 
+	(Version band ?VERSION_MASK) /= ?VERSION_1 ->
+	    throw(tProtocolException:new(?tProtocolException_BAD_VERSION,
+					 "Missing version identifier"));
+	true -> ok
+    end,
+    Type = Version band 16#000000ff,
+    Name  = readString(This),
+    Seqid = readI32(This),
+    { Name, Type, Seqid }.
+
+readMessageEnd(This) ->
+    This, % suppress unused warnings
+    ok.
+
+readStructBegin(This) ->
+    This, % suppress unused warnings
+    ok.
+
+readStructEnd(This) ->
+    This, % suppress unused warnings
+    ok.
+
+readFieldBegin(This) ->
+    Type = readByte(This),
+    if Type == ?tType_STOP ->
+	    { nil, Type, 0 }; % WATCH
+       true ->
+	    Id = readI16(This),
+	    { nil, Type, Id }
+    end.
+
+readFieldEnd(This) ->
+    This, % suppress unused warnings
+    ok.
+
+readMapBegin(This) ->
+    Ktype = readByte(This),
+    Vtype = readByte(This),
+    Size  = readI32(This),
+    { Ktype, Vtype, Size }.
+
+readMapEnd(This) ->
+    This, % suppress unused warnings
+    ok.
+
+readListBegin(This) ->
+    Etype = readByte(This),
+    Size  = readI32(This),
+    { Etype, Size }.
+
+readListEnd(This) ->
+    This, % suppress unused warnings
+    ok.
+
+readSetBegin(This) ->
+    Etype = readByte(This),
+    Size  = readI32(This),
+    { Etype, Size }.
+
+readSetEnd(This) ->
+    This, % suppress unused warnings
+    ok.
+
+% WATCH everything ... who knows what of this will work
+
+readBool(This) ->
+    Byte = readByte(This),
+    (Byte /= 0).
+
+readByte(This) ->
+    Trans = This#tBinaryProtocol.trans,
+    <<Val:8/integer-signed-big, _/binary>>  = ?M1(Trans, readAll, 1),
+    Val.
+
+readI16(This) ->
+    Trans = This#tBinaryProtocol.trans,
+    <<Val:16/integer-signed-big, _/binary>>  = ?M1(Trans, readAll, 2),
+    Val.
+
+readI32(This) ->
+    Trans = This#tBinaryProtocol.trans,
+    <<Val:32/integer-signed-big, _/binary>>  = ?M1(Trans, readAll, 4),
+    Val.
+
+readI64(This) ->
+    Trans = This#tBinaryProtocol.trans,
+    <<Val:64/integer-signed-big, _/binary>>  = ?M1(Trans, readAll, 8),
+    Val.
+
+readDouble(This) ->
+    Trans = This#tBinaryProtocol.trans,
+    <<Val:64/float-signed-big, _/binary>>  = ?M1(Trans, readAll, 8),
+    Val.
+
+readString(This) ->
+    Trans = This#tBinaryProtocol.trans,
+    Sz    = readI32(This),
+    binary_to_list(?M1(Trans, readAll, Sz)).
diff --git a/lib/erl/src-loose/protocol/tBinaryProtocol.hrl b/lib/erl/src-loose/protocol/tBinaryProtocol.hrl
new file mode 100644
index 0000000..cfd4836
--- /dev/null
+++ b/lib/erl/src-loose/protocol/tBinaryProtocol.hrl
@@ -0,0 +1,4 @@
+-define(VERSION_MASK, 16#FFFF0000).
+-define(VERSION_1, 16#80010000).
+-record(tBinaryProtocol, {trans}).
+
diff --git a/lib/erl/src-loose/protocol/tProtocol.erl b/lib/erl/src-loose/protocol/tProtocol.erl
new file mode 100644
index 0000000..a36ca11
--- /dev/null
+++ b/lib/erl/src-loose/protocol/tProtocol.erl
@@ -0,0 +1,69 @@
+-module(tProtocol).
+
+-include("thrift/thrift.hrl").
+-include("thrift/protocol/tProtocol.hrl").
+
+-export([new/1, skip/2]).
+
+skip_struct_loop(This) ->
+    { Name, Type, Id } = ?M0(This, readFieldBegin),
+    Name, Id, % suppress unused warnings
+    if
+	Type == ?tType_STOP ->
+	    ok;
+	true ->
+	    skip(This, Type),
+	    ?M0(This, readFieldEnd),
+
+	    %% this is here in original tprotocol.rb, but i think it's a bug
+	    % ?M0(This, readStructEnd),
+	    skip_struct_loop(This)
+    end.
+       
+skip_map_repeat(This, Ktype, Vtype, Times) ->
+    skip(This, Ktype),
+    skip(This, Vtype),
+    skip_map_repeat(This, Ktype, Vtype, Times-1).
+
+skip_set_repeat(This, Etype, Times) ->
+    skip(This, Etype),
+    skip_set_repeat(This, Etype, Times-1).
+
+new(Trans) ->
+    #tProtocol{trans=Trans}.
+
+skip(This, Type) ->
+    case Type of 
+	?tType_STOP   -> nil; % WATCH
+	?tType_BOOL   -> ?M0(This, readBool);
+	?tType_BYTE   -> ?M0(This, readByte);
+	?tType_I16    -> ?M0(This, readI16);
+	?tType_I32    -> ?M0(This, readI32);
+	?tType_I64    -> ?M0(This, readI64);
+	?tType_DOUBLE -> ?M0(This, readDouble);
+	?tType_STRING -> ?M0(This, readString);
+
+	?tType_STRUCT -> 
+	    ?M0(This, readStructBegin),
+	    skip_struct_loop(This),
+
+	    %% this isn't here in the original tprotocol.rb, but i think it's a bug
+	    ?M0(This, readStructEnd);
+
+	?tType_MAP ->
+	    {Ktype, Vtype, Size} = ?M0(This, readMapBegin),
+	    skip_map_repeat(This, Ktype, Vtype, Size),
+	    ?M0(This, readMapEnd);
+
+	?tType_SET -> 
+	    {Etype, Size} = ?M0(This, readSetBegin),
+	    skip_set_repeat(This, Etype, Size),
+	    ?M0(This, readSetEnd);
+
+	?tType_LIST ->
+	    {Etype, Size} = ?M0(This, readListBegin),
+	    skip_set_repeat(This, Etype, Size), % [sic] skipping same as for SET
+	    ?M0(This, readListEnd)
+    end.
+
+
diff --git a/lib/erl/src-loose/protocol/tProtocol.hrl b/lib/erl/src-loose/protocol/tProtocol.hrl
new file mode 100644
index 0000000..4385124
--- /dev/null
+++ b/lib/erl/src-loose/protocol/tProtocol.hrl
@@ -0,0 +1 @@
+-record(tProtocol, {trans}).
diff --git a/lib/erl/src-loose/protocol/tProtocolException.erl b/lib/erl/src-loose/protocol/tProtocolException.erl
new file mode 100644
index 0000000..b9aa48a
--- /dev/null
+++ b/lib/erl/src-loose/protocol/tProtocolException.erl
@@ -0,0 +1,9 @@
+-module(tProtocolException).
+-include("tProtocolException.hrl").
+-export([new/2, new/1, new/0]).
+
+new(Type, Message) ->
+    #tProtocolException{type=Type, message=Message}.
+
+new(Type) -> new(Type, nil).
+new()     -> new(?tProtocolException_UNKNOWN, nil).
diff --git a/lib/erl/src-loose/protocol/tProtocolException.hrl b/lib/erl/src-loose/protocol/tProtocolException.hrl
new file mode 100644
index 0000000..96bf3ac
--- /dev/null
+++ b/lib/erl/src-loose/protocol/tProtocolException.hrl
@@ -0,0 +1,8 @@
+-record(tProtocolException, {message, type}).
+
+-define(tProtocolException_UNKNOWN, 0).
+-define(tProtocolException_INVALID_DATA, 1).
+-define(tProtocolException_NEGATIVE_SIZE, 2).
+-define(tProtocolException_SIZE_LIMIT, 3).
+-define(tProtocolException_BAD_VERSION, 4).
+
diff --git a/lib/erl/src-loose/server/tServer.erl b/lib/erl/src-loose/server/tServer.erl
new file mode 100644
index 0000000..0c3182e
--- /dev/null
+++ b/lib/erl/src-loose/server/tServer.erl
@@ -0,0 +1,62 @@
+-module(tServer).
+
+-include("thrift/thrift.hrl").
+-include("thrift/protocol/tProtocol.hrl").
+-include("thrift/protocol/tBinaryProtocol.hrl").
+% -include("thrift/transport/tTransport.hrl").
+-include("tServer.hrl").
+
+-export([new/3, serve/1]).
+
+% now processor is the module with process_*, not an object
+
+new(ProcessorModule, HandlerModule, ServerTransport) ->
+    #tServer{processorModule=ProcessorModule, 
+	     handlerModule=HandlerModule,
+	     serverTransport=ServerTransport}.
+
+serverTransport(This) ->
+    This#tServer.serverTransport.
+
+serve(This) ->
+    ST1 = ?M0(serverTransport(This), listen_MUTABLE),
+    This1 = This#tServer{serverTransport=ST1},
+    serve_loop(This1).
+
+processorModule(This) ->
+    This#tServer.processorModule.
+
+handlerModule(This) ->
+    This#tServer.handlerModule.
+
+serve_loop(This) ->
+    io:format("~nready.~n", []),
+    Client = ?M0(serverTransport(This), accept_MUTABLE),
+    This1  = This#tServer{serverTransport=Client},
+
+    Trans  = Client, % factory
+    Prot   = tBinaryProtocol:new(Trans),
+    serve_loop_loop(This1, Prot), % giggle loop?
+    ?M0(Trans, close_MUTABLE), % don't "assign" ... discard
+    serve_loop(This).
+    
+serve_loop_loop(This, Prot) ->
+    Next = try
+	Val = (processorModule(This)):process(handlerModule(This), Prot, Prot),
+	io:format("request processed: rv=~p~n", [Val]),
+	loop
+    catch 
+	%% TODO(cpiro) case when is_record(...) to pick out our exception
+	%% records vs. normal erlang throws
+	{tTransportException,_,_} ->
+	    io:format("tTransportException (normal-ish?)~n", []),
+	    close;
+	E ->
+	    io:format("EXCEPTION: ~p~n", [E]),
+	    close
+    end,
+    case Next of 
+	loop -> serve_loop_loop(This, Prot);
+	close -> ok
+    end.
+
diff --git a/lib/erl/src-loose/server/tServer.hrl b/lib/erl/src-loose/server/tServer.hrl
new file mode 100644
index 0000000..23da029
--- /dev/null
+++ b/lib/erl/src-loose/server/tServer.hrl
@@ -0,0 +1 @@
+-record(tServer, {processorModule, handlerModule, serverTransport}).
diff --git a/lib/erl/src-loose/tApplicationException.erl b/lib/erl/src-loose/tApplicationException.erl
new file mode 100644
index 0000000..c6453a3
--- /dev/null
+++ b/lib/erl/src-loose/tApplicationException.erl
@@ -0,0 +1,64 @@
+-module(tApplicationException).
+
+-include("thrift.hrl").
+% -include("tApplicationException.hrl").
+
+-export([new/0, new/1, new/2, read/2, write/2]).
+
+new(Type, Message) ->
+    #tApplicationException{type=Type, message=Message}.
+
+new()     -> new(?tApplicationException_UNKNOWN, nil). % WATCH
+new(Type) -> new(Type, nil). % WATCH
+
+read(This, Iprot) ->
+    ?M0(Iprot, readStructBegin),
+    read_while_loop(This, Iprot),
+    ?M0(Iprot, readStructEnd),
+    This.
+
+read_while_loop(This, Iprot) ->
+    {_, Ftype, Fid} = ?M0(Iprot, readFieldBegin), % field = {fname, ftype, fid}
+
+    if 
+	Ftype == ?tType_STOP ->
+	    This;
+	(Fid == 1) and (Ftype == ?tType_STRING) ->
+	    This1 = This#tApplicationException{message=?M0(Iprot, readString)},
+	    ?M0(Iprot, readFieldEnd),
+	    read_while_loop(This1, Iprot);
+
+	Fid == 1 ->
+	    ?M0(Iprot, skip),
+	    ?M0(Iprot, readFieldEnd),
+	    read_while_loop(This, Iprot);
+
+	(Fid == 2) and (Ftype == ?tType_I32) ->
+	    This1 = This#tApplicationException{type=?M0(Iprot, readI32)},
+	    ?M0(Iprot, readFieldEnd),
+	    read_while_loop(This1, Iprot);
+
+        true -> 
+	    ?M0(Iprot, skip),
+	    ?M0(Iprot, readFieldEnd),
+	    read_while_loop(This, Iprot)
+    end.
+
+write(This, Oprot) ->	
+    ?M1(Oprot, writeStructBegin, "tApplicationException"),
+    Message = This#tApplicationException.message,
+    Type = This#tApplicationException.type,
+    if	Message /= undefined ->
+	    ?M3(Oprot, writeFieldBegin, "message", ?tType_STRING, 1),
+	    ?M1(Oprot, writeString, Message),
+	    ?M0(Oprot, writeFieldEnd);
+        true -> ok
+    end,
+    if  Type /= undefined -> 
+	    ?M3(Oprot, writeFieldBegin, "type", ?tType_I32, 2),
+	    ?M1(Oprot, writeI32, Type),
+	    ?M0(Oprot, writeFieldEnd);
+        true -> ok
+    end,
+    ?M0(Oprot, writeFieldStop),
+    ?M0(Oprot, writeStructEnd).
diff --git a/lib/erl/src-loose/tApplicationException.hrl b/lib/erl/src-loose/tApplicationException.hrl
new file mode 100644
index 0000000..ab1f401
--- /dev/null
+++ b/lib/erl/src-loose/tApplicationException.hrl
@@ -0,0 +1,9 @@
+% TApplicationException
+-define(tApplicationException_UNKNOWN, 0).
+-define(tApplicationException_UNKNOWN_METHOD, 1).
+-define(tApplicationException_INVALID_MESSAGE_TYPE, 2).
+-define(tApplicationException_WRONG_METHOD_NAME, 3).
+-define(tApplicationException_BAD_SEQUENCE_ID, 4).
+-define(tApplicationException_MISSING_RESULT, 5).
+
+-record(tApplicationException, {message, type=?tApplicationException_UNKNOWN}).
diff --git a/lib/erl/src-loose/tException.erl b/lib/erl/src-loose/tException.erl
new file mode 100644
index 0000000..3a4a9e8
--- /dev/null
+++ b/lib/erl/src-loose/tException.erl
@@ -0,0 +1,7 @@
+-module(tException).
+-include("tException.hrl").
+-export([new/0]).
+
+new() ->
+    #tException{}.
+
diff --git a/lib/erl/src-loose/tException.hrl b/lib/erl/src-loose/tException.hrl
new file mode 100644
index 0000000..6b10551
--- /dev/null
+++ b/lib/erl/src-loose/tException.hrl
@@ -0,0 +1 @@
+-record(tException, {message}).
diff --git a/lib/erl/src-loose/thrift.hrl b/lib/erl/src-loose/thrift.hrl
new file mode 100644
index 0000000..845d61c
--- /dev/null
+++ b/lib/erl/src-loose/thrift.hrl
@@ -0,0 +1,36 @@
+-define(CLASS(Obj), element(1,Obj)).
+
+-define(M0(Obj, Method), ((?CLASS(Obj)):Method(Obj))).
+-define(M1(Obj, Method, Arg1), ((?CLASS(Obj)):Method(Obj, Arg1))).
+-define(M2(Obj, Method, Arg1, Arg2), ((?CLASS(Obj)):Method(Obj, Arg1, Arg2))).
+-define(M3(Obj, Method, Arg1, Arg2, Arg3), ((?CLASS(Obj)):Method(Obj, Arg1, Arg2, Arg3))).
+-define(M4(Obj, Method, Arg1, Arg2, Arg3, Arg4), ((?CLASS(Obj)):Method(Obj, Arg1, Arg2, Arg3, Arg4))).
+-define(M5(Obj, Method, Arg1, Arg2, Arg3, Arg4, Arg5), ((?CLASS(Obj)):Method(Obj, Arg1, Arg2, Arg3, Arg4, Arg5))).
+-define(M6(Obj, Method, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6), ((?CLASS(Obj)):Method(Obj, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6))).
+
+-define(ATTR(X), This#?MODULE.X).
+
+%% TType
+-define(tType_STOP, 0).
+-define(tType_VOID, 1).
+-define(tType_BOOL, 2).
+-define(tType_BYTE, 3).
+-define(tType_DOUBLE, 4).
+-define(tType_I16, 6).
+-define(tType_I32, 8).
+-define(tType_I64, 10).
+-define(tType_STRING, 11).
+-define(tType_STRUCT, 12).
+-define(tType_MAP, 13).
+-define(tType_SET, 14).
+-define(tType_LIST, 15).
+
+% tmessagetype
+-define(tMessageType_CALL, 1).
+-define(tMessageType_REPLY, 2).
+-define(tMessageType_EXCEPTION, 3).
+
+% TProcessor
+% ?
+
+-include("thrift/tApplicationException.hrl").
diff --git a/lib/erl/src-loose/transport/tBufferedTransport.erl b/lib/erl/src-loose/transport/tBufferedTransport.erl
new file mode 100644
index 0000000..1cc809d
--- /dev/null
+++ b/lib/erl/src-loose/transport/tBufferedTransport.erl
@@ -0,0 +1,34 @@
+-module(tBufferedTransport).
+
+-include("thrift/thrift.hrl").
+-include("thrift/transport/tBufferedTransport.hrl").
+
+-export([new/1, isOpen/1, open/1, close/1, read/2, write_MUTABLE/2, flush_MUTABLE/1]).
+
+new(Transport) ->
+    #tBufferedTransport{transport=Transport, wbuf=""}.
+
+transport(This) -> % local accessor
+    This#tBufferedTransport.transport.
+
+isOpen(This) ->
+    ?M0(transport(This), isOpen).
+
+open(This) ->
+    ?M0(transport(This), open).
+
+close(This) ->
+    ?M0(transport(This), close).
+
+read(This, Sz) ->
+    ?M1(transport(This), read, Sz).
+
+write_MUTABLE(This, Buf) -> % be sure to rebind This to the retval
+    Wbuf = This#tBufferedTransport.wbuf,
+    This#tBufferedTransport{wbuf=Wbuf++Buf}. % TODO: ++ efficiency?
+
+flush_MUTABLE(This) -> % be sure to rebind This to the retval
+    Wbuf = This#tBufferedTransport.wbuf,
+    ?M1(transport(This), write, Wbuf),
+    ?M0(transport(This), flush),
+    This#tBufferedTransport{wbuf=""}. % TODO: ++ efficiency?
diff --git a/lib/erl/src-loose/transport/tBufferedTransport.hrl b/lib/erl/src-loose/transport/tBufferedTransport.hrl
new file mode 100644
index 0000000..d8d71e1
--- /dev/null
+++ b/lib/erl/src-loose/transport/tBufferedTransport.hrl
@@ -0,0 +1 @@
+-record(tBufferedTransport, {transport, wbuf}).
diff --git a/lib/erl/src-loose/transport/tServerSocket.erl b/lib/erl/src-loose/transport/tServerSocket.erl
new file mode 100644
index 0000000..239af6e
--- /dev/null
+++ b/lib/erl/src-loose/transport/tServerSocket.erl
@@ -0,0 +1,44 @@
+-module(tServerSocket).
+-include("tServerSocket.hrl").
+
+-export([new/1, listen_MUTABLE/1, accept_MUTABLE/1, close/1]).
+
+new(Port) ->
+    #tServerSocket{port=Port, handle=nil}.
+
+listen_MUTABLE(This) ->
+    Port = This#tServerSocket.port,
+    Options = [binary, {packet, 0}, {active, false}], % was []
+
+    case gen_tcp:listen(Port, Options) of 
+	{ok, ListenSocket} ->
+	    This#tServerSocket{handle=ListenSocket}
+	% {error, _} -> 
+	% TODO: no error handling in Ruby version?
+    end.
+
+accept_MUTABLE(This) ->
+    if 
+	This#tServerSocket.handle /= nil ->
+	    case gen_tcp:accept(This#tServerSocket.handle) of
+		{ok, Socket} ->
+		    tSocket:setHandle_MUTABLE( tSocket:new(), Socket )
+	        % {error, _} -> 
+                % TODO: no error handling in Ruby version?
+	    end;
+	true ->
+	    nil
+    end.
+
+close(This) ->
+    if 
+ 	This#tServerSocket.handle /= nil ->
+	    case gen_tcp:close(This#tServerSocket.handle) of 
+		ok ->
+		    ok
+	        % {error, _} -> 
+                % TODO: no error handling in Ruby version?
+	    end;
+	true ->
+	    ok
+    end.
diff --git a/lib/erl/src-loose/transport/tServerSocket.hrl b/lib/erl/src-loose/transport/tServerSocket.hrl
new file mode 100644
index 0000000..34ed320
--- /dev/null
+++ b/lib/erl/src-loose/transport/tServerSocket.hrl
@@ -0,0 +1 @@
+-record(tServerSocket, {port, handle}).
diff --git a/lib/erl/src-loose/transport/tSocket.erl b/lib/erl/src-loose/transport/tSocket.erl
new file mode 100644
index 0000000..850c3b9
--- /dev/null
+++ b/lib/erl/src-loose/transport/tSocket.erl
@@ -0,0 +1,96 @@
+-module(tSocket).
+
+-include("thrift/thrift.hrl").
+-include("thrift/transport/tTransportException.hrl").
+% -include("thrift/transport/tTransport.hrl").
+-include("thrift/transport/tSocket.hrl").
+
+-export([new/0, new/1, new/2, setHandle_MUTABLE/2, open_MUTABLE/1, isOpen/1, write/2, read/2, close_MUTABLE/1, readAll/2]).
+
+new(Host, Port) ->
+    #tSocket{host=Host, port=Port, handle=nil}. % WATCH
+
+new()     -> new("localhost", 9090).
+new(Host) -> new(Host, 9090).
+    
+setHandle_MUTABLE(This, Handle) ->
+    This#tSocket{handle=Handle}.
+
+open_MUTABLE(This) -> 
+    Host = This#tSocket.host,
+    Port = This#tSocket.port,
+    Options = [],
+
+    case gen_tcp:connect(Host, Port, Options) of
+	{error, _} ->
+	    throw(tTransportException:new(
+		    ?tTransportException_NOT_OPEN,
+		    "Could not connect to " ++ Host ++ ":" ++ Port)
+		 ),
+	    {error, This}; % cpiro not reached?
+	{ok, Socket} ->
+	    {ok, This#tSocket{handle=Socket}}
+    end.
+
+handle(This) ->
+    This#tSocket.handle.
+
+isOpen(This) ->
+    handle(This) /= nil.
+
+write(This, Str) ->
+    Val = gen_tcp:send(handle(This), Str),
+
+    %% io:format("WRITE |~p|(~p)~n", [Str,Val]),
+    
+    case Val of
+	{error, _} ->
+	    throw(tTransportException:new(?tTransportException_NOT_OPEN, "in write"));
+	ok ->
+	    ok
+    end.
+
+read(This, Sz) ->
+    case gen_tcp:recv(handle(This), Sz) of
+	{ok, []} ->
+	    { Host, Port } = { This#tSocket.host, This#tSocket.port },
+	    throw(tTransportException:new(?tTransportException_UNKNOWN, "TSocket: Could not read " ++ Sz ++ "bytes from " ++ Host ++ ":" ++ Port));
+	{ok, Data} ->
+	    Data;
+	{error, Error} ->
+	    io:format("in tSocket:read/2: gen_tcp:recv(~p, ~p) => {error, ~p}~n",
+		      [handle(This), Sz, Error]),
+	    throw(tTransportException:new(?tTransportException_NOT_OPEN, "in tSocket:read/2: gen_tcp:recv"))
+	end.
+	    
+close_MUTABLE(This) ->
+    if
+	This#tSocket.handle == nil ->
+	    This;
+	true ->
+	    gen_tcp:close(handle(This)),
+	    This#tSocket{handle=nil}
+    end.
+
+readAll(This, Sz) ->
+    readAll_loop(This, Sz, "", 0).
+
+readAll_loop(This, Sz, Buff, Have) ->
+    if 
+	Have < Sz ->
+	    Chunk = ?M1(This, read, Sz - Have),
+
+	    %% man gen_tcp:
+	    %% exactly Length bytes are returned, or an error; 
+	    %% possibly discarding less than Length bytes of data when 
+	    %% the socket gets closed from the other side.
+
+	    %% io:format("READ |~p|~n", [Chunk]),
+
+	    Have1 = Have + (Sz-Have), % length(Chunk)
+	    Buff1 = Buff ++ Chunk, % TODO: ++ efficiency?
+	    readAll_loop(This, Sz, Buff1, Have1);
+	true ->
+	    Buff
+    end.
+
diff --git a/lib/erl/src-loose/transport/tSocket.hrl b/lib/erl/src-loose/transport/tSocket.hrl
new file mode 100644
index 0000000..dc1cc20
--- /dev/null
+++ b/lib/erl/src-loose/transport/tSocket.hrl
@@ -0,0 +1 @@
+-record(tSocket, {host, port, handle}).
diff --git a/lib/erl/src-loose/transport/tTransport.erl b/lib/erl/src-loose/transport/tTransport.erl
new file mode 100644
index 0000000..91b7228
--- /dev/null
+++ b/lib/erl/src-loose/transport/tTransport.erl
@@ -0,0 +1,4 @@
+-module(tTransport).
+
+-include("thrift/transport/tTransportException.hrl").
+
diff --git a/lib/erl/src-loose/transport/tTransportException.erl b/lib/erl/src-loose/transport/tTransportException.erl
new file mode 100644
index 0000000..b31bb20
--- /dev/null
+++ b/lib/erl/src-loose/transport/tTransportException.erl
@@ -0,0 +1,15 @@
+-module(tTransportException).
+
+-include("thrift/thrift.hrl").
+-include("thrift/transport/tTransportException.hrl").
+
+-export([new/0, new/1, new/2, message/1]).
+
+new(Type, Message) ->
+    #tTransportException{type = Type, message = Message}.
+
+new()     -> new(?tTransportException_UNKNOWN, nil). % WATCH
+new(Type) -> new(Type, nil). % WATCH
+
+message(This) ->
+    ?ATTR(message).
diff --git a/lib/erl/src-loose/transport/tTransportException.hrl b/lib/erl/src-loose/transport/tTransportException.hrl
new file mode 100644
index 0000000..fa8554b
--- /dev/null
+++ b/lib/erl/src-loose/transport/tTransportException.hrl
@@ -0,0 +1,7 @@
+-define(tTransportException_UNKNOWN, 0).
+-define(tTransportException_NOT_OPEN, 1).
+-define(tTransportException_ALREADY_OPEN, 2).
+-define(tTransportException_TIMED_OUT, 3).
+-define(tTransportException_END_OF_FILE, 4).
+
+-record(tTransportException, {type, message}).
diff --git a/lib/erl/thrift b/lib/erl/thrift
new file mode 120000
index 0000000..1ab4c5c
--- /dev/null
+++ b/lib/erl/thrift
@@ -0,0 +1 @@
+src-loose
\ No newline at end of file