View
168
Download
3
Category
Preview:
Citation preview
Xtreams
'Don't cross the streams!'
Michael Lucas-SmithMartin Kobetic
Cincom Smalltalk EngineeringSmalltalk Solutions 2011
Status
Trunk: StableDocumentation: http://code.google.com/p/xtreamsLicense: MITProject started: November 30th 2007
PlatformsVisualWorksSqueak and Pharo (Nicolas Cellier)Gemstone (Dale Henrichs)Slate (Brian Rice)
... thanks to all who have provided feedbackand patches
Why?
simplification and consistencyIncomplete vs atEndpositioning and 'surprising' buffersread and write are different
compositionno deep class hierarchy any more
capabilitiestransformationssubstreams
scalabilitychunks vs elementsrecycling
Structure
coreDefines the API and Core Classes.Support package is for portability.
terminalsUnique to each platform, wraps the source and destination objects in to streams.
transformscollecting, rejecting, selecting...character encodingbinary transformationsobject marshaling
Structure
substreams / parsinglimiting, element or subcollection endingslicing and stitchingparsing expression grammarsmultiplexing (experimental)
xtrascompressioncryptographychunking
applicationsSSH2 (Xtreams-SSH2)IRC (Xtreams-IRC)EXIF Metadata (Xtreams-Photo)
reading / writing
collections'example' reading.OrderedCollection new writing.Transcript writing.
(RingBuffer new: 4) writing.SharedQueue new writing.
reading / writing
externals'example.txt' asFilename reading.OS.Stdin reading.(External.CIntegerType unsignedChar
gcMalloc: 50) writing.(OS.SocketAccessor
newTCPclientToHost: 'localhost'port: 1234) reading.
transcendentals[0] reading. /dev/zero[:x | ] writing. /dev/nullKernel.ObjectMemory reading.Random new reading.
API
commonIncomplete.stream terminal.stream close.stream isReadable; isWritable; isPositionable.
readinginput get.input read: 5.input read: 5 into: collection.input read: 5 into: collection at: 2.input rest.
do:, do:separatedBy:, select:, reject:, collect:, fold:,detect:(ifNone:), inject:into:, groupedBy:
API
writingoutput put: 'example'.output write: $x.output write: input.output write: 'example'.output write: 4 from: input.output write: 4 from: 'example'.output write: 3 from: 'example' at: 4.output conclusion.
output insert: 'example'.output insert: 4 from: 'example'.output insert: 3 from: 'example' at: 4.
output print: #( 1 2 3 ).output cr; bell; q; qq; space; space: 2; tab; tab: 3
API
seekingpositionable := stream positioning.
transform a non-positionable stream in to a positionable stream
stream ++ 200.seek forward from where we are
stream -- 200.seek backward from where we are
stream += 200.seek forward from the start of the stream
stream -= 0. (skip to end)seek backward from the end of the stream
API
seekingstream explore: [stream read: 2].
seek within the block, but return to where we were once done
stream position. stream position: 4.change the position of the stream
stream available.elements left to consume on the stream(not to be mistaken with data left in socket buffer)
stream length.elements in the stream
QA
Questions?
Transforms
Collection Stylecollecting:, selecting:, injecting:into:, doing:, ...
Specialized Transformsencoding:, encodingBase64, encodingHex,compressing, en/decrypting:key:iv:, hashing:interpreting:, marshaling
General Transformstransforming: [ :in :out | out put: in get ]
Collection Transforms
random := Random new reading.random := random collecting: [ :f | (f * 256) floor ].random contentsSpecies: ByteArray.random read: 10.
current := 0.integers := [current := current + 1] reading.integers read: 10.even := integers selecting: [ :i | i even ].even read: 10.
Character Encoding
input := 'xtreams.cha' asFilename reading.input := input encoding: #utf8.input read: 50.input close.
(#[13 10 10 13] reading encoding: #ascii) rest.(ByteArray new writing encoding: #ascii)
cr; conclusion
Composition / Stacking
ones := [ 1 ] reading.twoAndUp := ones
injecting: 1into: [ :previous :one | previous + one ].
sieve := OrderedCollection new.primes := twoAndUp rejecting: [ :i |
(sieve anySatisfy: [ :p | i \\ p = 0 ])ifTrue: [ true ]ifFalse: [ sieve add: i. false ] ].
primes read: 10.
Morse Code
Message... -- .- .-.. .-.. - .- .-.. -.-
DecodingTree - << * >> . T E M N A I O G K D W R U S Q Z Y C X B J P L F V H
($ ($T ($M ($O) ($G ($Q) ($Z))) ($N ($K ($Y) ($C)) ($D ($X) ($B))) ($E ($A ($W ($J) ($P)) ($R () ($L))) ($I ($U () ($F)) ($S ($V) ($H)))
Morse Code Decoding
($ ($T ($M ($O) ($G ($Q) ($Z))) ($N ($K ($Y) ($C)) ($D ($X) ($B))) ($E ($A ($W ($J) ($P)) ($R () ($L))) ($I ($U () ($F)) ($S ($V) ($H)))
('... -- .- .-.. .-.. - .- .-.. -.- ' readingtransforming: [ :in :out || node beep |
node := MorseTree.[ beep := in get.
beep = Character space ] whileFalse: [
node := beep = $.ifTrue: [ node at: 3 ]ifFalse: [ node at: 2 ] ].
out put: node first ]) rest
Morse Code Encoding
(String new writingtransforming: [ :in :out |
out write: (Morse at: in get);space ]
) write: 'SMALLTALK';conclusion
QA
Questions?
Substreams
* sentences in text* parts of messages in a protocol* files in an archive
size(Object comment reading limiting: 10) rest.
bounding criteria(Object comment reading ending: $.) rest.(Object comment reading ending: [:e | '.?!' includes:(Object comment reading ending: ' is the') rest.
streams of substreamsslicing streams into substreamsstitching streams from substreams
Substreams - limiting:
output := String new writing.Number withAllSubclasses do: [ :class |
[ (output limiting: 40) write: class comment.] on: Incomplete do: [ :ex | output -- 3; write: '...' ].output cr ].
output conclusion.
Substreams - ending:
output := String new writing.Number withAllSubclasses do: [ :class |
[ (output ending: $. inclusive: true)write: class comment
] on: Incomplete do: [].output cr ].
output conclusion
Slicing
readinginput := 'xtreams.cha' asFilename reading.input := input encoding: #utf8.[ lines := (input ending: Character cr) slicing.
(lines ++ 10000; get) rest] ensure: [ input close ].
writingoutput := String new writing.blurbs := (output limiting: 40) slicing.Number withAllSubclasses do: [ :class |
[ blurbs get write: class comment.] on: Incomplete do: [ :ex | output -- 3; write: '...' output cr ].
output conclusion.
Slices of Slices
readinginput := 'aaa#bb#c##!1#22#33#444' reading.messages := (input ending: $!) slicing.parts := (messages get ending: $#) slicing.parts collect: [ :p | p rest ].
writingoutput := String new writing.messages := (output closing: [ output put: $! ])
slicing.#((aa bb cc dd ee) (xxx yy z)) do: [ :m |
message := messages get.parts := (message closing: [ message put: $# ])
slicing.m do: [ :p | parts get write: p ] ].
output conclusion
Stitching Reads
((1 to: 5) reading, (6 to: 10) reading) rest.((1 to: 10) reading limiting: 3) slicing stitching rest.
files := '..' asFilename reading.[ | fn | fn := files get.
fn isDirectory ifTrue: [ files := fn reading, files ].fn
] reading collect: [ :f | f asString ].
directories := ElasticBuffer new: 10 class: Array.directories put: '..' asFilename.[ directories get reading doing: [ :filename |
filename isDirectory ifTrue: [directories put: filename]]
] reading stitching collect: [ :f | f asString ].
Stitching Writes
output := ByteArray new writing.buffer := RingBuffer on: (ByteArray new: 5).[ (buffer writing limiting: buffer cacheSize)
closeBlock: [output put: buffer readSize;
write: buffer ];yourself
] reading stitchingwrite: (1 to: 12);close.
output conclusion
QA
Questions?
Back to Binary
Stream --> ObjectsInterpreting streamsObject marshalingParsing Expression GrammarsErlang 'Bit Syntax'
Objects --> StreamInterpreting streamsObject marshaling
Interpreting
stream interpreting: #long.stream interpreting: #signedLonglong_be.
(input interpreting: #float) read: 10.(output interpreting: #double) put: Double pi.
Marshaling
goalsCross-Smalltalk (not achieved yet)Space EfficiencySpeed Efficiency
to replace...BOSSOpentalk-STSTParcels (sort of)SIXX
Marshaling
ObjectMarshalerSeparate from the Read/Write streams.Pluggable marshaling strategy, for different protocolsUses Pragmas to allow extensions with a computed hash to detect different versions.
AnalysisRead without instantiating objects to diagnose a stream.
Marshaling
output := ByteArray new writing marshaling.output put: 100 asValue.
output conclusion = #[83 84 83 84 20 4 21 199 91 7 32 29 82 111 111 116 46 83 109 97 108 108 116 97 108 107 46 85 73 46 86 97 108 117 101 72 111 108 100 101 114 0 1 28 22 100]
Marshaling
(Xtreams.ObjectAnalyseStream on:output conclusion reading) rest =
'0+10 header: #[83 84 83 84 20 4 105 117 236 8]10+36 record
10+1 class id: 3211+31 class description: UI.ValueHolder42+1 object id: 143+3 object: UI.ValueHolder
43+1 dependents43+1 class id: 28 Core.UndefinedObject
43+1 class id: 2844+0 nil
44+2 value44+2 class id: 22 Core.SmallInteger
44+1 class id: 2245+1 byte integer: 100'
Marshaling
output conclusion reading marshaling get.a ValueHolder on: 100
Parsing
Parsing Expression GrammarsErlang 'bit syntax'Reusable GrammarsGrammar composition
Parsing
Parsing Expression Grammars
grammar := 'Sentence <- Whitespace? (Word / Punctuation)* Word <- [a-zA-Z'']+Punctuation <- [,;:()]'.
sentenceParser := PEG.Parser parserPEGparse: 'Grammar'stream: grammaractor: PEG.ParserParser new
Parsing
ApplicationsTransformed Namespaced class names of Xtreams during import/export via Monticello using the Smalltalk PEG grammar with an Actor that contained a single method.
Implemented an IRC client where data read from the client stream was parsed with a grammar and interpreted using a single method on the IRC client itself. The whole IRC implementation is one class.
Parsing
ApplicationsTransformed Smalltalk, Javascript, XML, CSS3 grammars in to a Javascript lexer to perform syntax color highlighting in javascript in the web browser for WebVelocity 1.1
This presentation. The slides of this presentation are built by parsing Smalltalk methods comprised of wiki syntax in comments and smalltalk code. The wiki grammar is used with an actor to generate Text objects that display what you see right now.
Parsing
Parsing Expression GrammarsPEG: character streams onlyRead one character: .Read one of many characters: [a-zA-Z]Read a sequence of characters: ''TITLE''
Moving beyond... Grammar CompositionCompile grammar into bytecode on lightweight classRead one character: {character/utf8}Read one byte: {integer/unsigned/little}:8Read one of many characters in utf16:
[a-zA-Z]/utf16Read a sequence of characters in utf8:
''TITLE''/utf8
QA
Questions?
Xtras
Various non-core transformations
Compressioncalls ZLib
Cryptographycalls BCrypt (CNG) on Windowscalls OpenSSL's libcrypto everywhere elsehashing (MD5, SHA1, SHA256, SHA512, ...)hashing with key (HMAC with MD5, SHA1, ...)encryption (RC4, AES, DES, ...)
HTTP Chunking
Xtras - Examples
hashing(ObjectMemory imageFilename reading
hashing: 'MD5') -= 0; close; digest.
encryptionkey := random read: 16.((String new writing
encodingBase64encrypting: 'RC4' key: key)compressingencoding: #utf8
) write: Object comment;conclusion.
Xtras - SSH2
doestransportauthentication: public-key, passwordchannel/session managementdata transferchannel requests: exec, env, exit-statusscp
does notterminal session/emulation: pty-req, shellport forwarding (tunnels)scp atime/mtime
Xtras - SSH2
keys := SSH2KeysTest sampleKeys.config := SSH2Configuration new keys: keys.server := ('-thishost-' asIPv4: 2222) listen accepting.[ (SSH2ServerConnection on: server get)
configuration: config;when: SSH2Announcementdo: [ :m | Transcript cr; print: m ];accept;waitForDisconnect;close
] ensure: [ server close. keys release ]
Xtras - SSH2
home := '$(HOME)' asLogicalFileSpecification asFilenamuser := home tail.keys := SSH2Keys fromUser: home.config := SSH2Configuration new keys: keys.client := ('localhost' asIPv4: 22) connect.client := SSH2ClientConnection on: client.client configuration: config.client when: SSH2Announcement
do: [ :m | Transcript cr; print: m ].[ service := client connect: user.
session := service session.[ session exec: 'ls -l'] ensure: [ session close ]
] ensure: [ client close. keys release ]
QA
Questions?
Recommended