diff options
author | William Joye <wjoye@cfa.harvard.edu> | 2016-10-27 19:39:39 (GMT) |
---|---|---|
committer | William Joye <wjoye@cfa.harvard.edu> | 2016-10-27 19:39:39 (GMT) |
commit | ea28451286d3ea4a772fa174483f9a7a66bb1ab3 (patch) | |
tree | 6ee9d8a7848333a7ceeee3b13d492e40225f8b86 /tcllib/modules/bee | |
parent | b5ca09bae0d6a1edce939eea03594dd56383f2c8 (diff) | |
parent | 7c621da28f07e449ad90c387344f07a453927569 (diff) | |
download | blt-ea28451286d3ea4a772fa174483f9a7a66bb1ab3.zip blt-ea28451286d3ea4a772fa174483f9a7a66bb1ab3.tar.gz blt-ea28451286d3ea4a772fa174483f9a7a66bb1ab3.tar.bz2 |
Merge commit '7c621da28f07e449ad90c387344f07a453927569' as 'tcllib'
Diffstat (limited to 'tcllib/modules/bee')
-rw-r--r-- | tcllib/modules/bee/ChangeLog | 116 | ||||
-rw-r--r-- | tcllib/modules/bee/bee.bench | 79 | ||||
-rw-r--r-- | tcllib/modules/bee/bee.man | 343 | ||||
-rw-r--r-- | tcllib/modules/bee/bee.pcx | 81 | ||||
-rw-r--r-- | tcllib/modules/bee/bee.tcl | 990 | ||||
-rw-r--r-- | tcllib/modules/bee/bee.test | 384 | ||||
-rw-r--r-- | tcllib/modules/bee/example.torrent | bin | 0 -> 22267 bytes | |||
-rw-r--r-- | tcllib/modules/bee/pkgIndex.tcl | 4 |
8 files changed, 1997 insertions, 0 deletions
diff --git a/tcllib/modules/bee/ChangeLog b/tcllib/modules/bee/ChangeLog new file mode 100644 index 0000000..630a16c --- /dev/null +++ b/tcllib/modules/bee/ChangeLog @@ -0,0 +1,116 @@ +2013-02-01 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.15 ======================== + * + +2011-12-13 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.14 ======================== + * + +2011-01-24 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.13 ======================== + * + +2009-12-07 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.12 ======================== + * + +2008-12-12 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.11.1 ======================== + * + +2008-10-16 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.11 ======================== + * + +2008-06-12 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * bee.pcx: New file. Syntax definitions for the public commands of + the bee package. + +2007-09-12 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.10 ======================== + * + +2007-03-19 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * bee.man: Fixed all warnings due to use of now deprecated + commands. Added a section about how to give feedback. + +2006-10-03 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.9 ======================== + * + +2006-01-22 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * bee.test: More boilerplate simplified via use of test support. + +2006-01-19 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * bee.test: Hooked into the new common test support code. + +2005-10-21 Andreas Kupries <andreask@activestate.com> + + * bee.test: Fixed typo. + +2005-10-18 Andreas Kupries <andreask@activestate.com> + + * bee.bench: New file, benchmarks, only basics for now. + +2005-10-06 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.8 ======================== + * + +2004-10-05 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.7 ======================== + * + +2004-09-27 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * bee.man: Cleared up version confusion. This package + * bee.test: definitely requires 8.4. Fixed in package + * pkgIndex.tcl: index, docs, added boilerplate abort to + testsuite. + + * bee.test: Fixed problem with testsuite, cannot use viewFile, + does not do binary. + +2004-08-15 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * bee.tcl: Typo police. + * bee.man: + * bee.test: + +2004-06-23 Andreas Kupries <andreas_kupries@users.sourceforge.bet> + + * bee.man: Polished the documentation. + +2004-06-21 Andreas Kupries <andreas_kupries@users.sourceforge.bet> + + * bee.tcl: Completed the implementation. + * bee.man: Completed documentation. + * bee.test: Completed testsuite. + +2004-06-18 Andreas Kupries <andreas_kupries@users.sourceforge.bet> + + * New module: BEE de- and encoding. BEE is the serialization + format used by BitTorrent for its data and protocol messages. diff --git a/tcllib/modules/bee/bee.bench b/tcllib/modules/bee/bee.bench new file mode 100644 index 0000000..2fb55fe --- /dev/null +++ b/tcllib/modules/bee/bee.bench @@ -0,0 +1,79 @@ +# -*- tcl -*- +# Tcl Benchmark File +# +# This file contains a number of benchmarks for the 'bee' module. +# This allow developers to monitor/gauge/track package performance. +# +# (c) 2005 Andreas Kupries <andreas_kupries@users.sourceforge.net> + +# We need at least version 8.2 for the package and thus the +# benchmarks. + +if {![package vsatisfies [package provide Tcl] 8.4]} { + return +} + +# ### ### ### ######### ######### ######### ########################### +## Setting up the environment ... + +package forget bee +catch {namespace delete ::bee} +source [file join [file dirname [info script]] bee.tcl] + +# ### ### ### ######### ######### ######### ########################### +## Benchmarks. + +foreach n { + -10 -100 -1000 -10000 -100000 -1000000 + 0 + 10 100 1000 10000 100000 1000000 +} { + bench -desc "BEE encode Number $n" -body { + bee::encodeNumber $n + } + + bench -desc "BEE decode Number $n" -pre { + set str [bee::encodeNumber $n] + } -body { + bee::decode $str + } -post { + unset str + } + + bench -desc "BEE decodeIndices Number $n" -pre { + set str [bee::encodeNumber $n] + } -body { + bee::decodeIndices $str + } -post { + unset str + } +} + +foreach n {10 100 1000 10000} { + bench -desc "BEE encode String $n" -pre { + set str [string repeat X $n] + } -body { + bee::encodeString $str + } -post { + unset str + } + + bench -desc "BEE decode String $n" -pre { + set str [bee::encodeString [string repeat X $n]] + } -body { + bee::decode $str + } -post { + unset str + } + + bench -desc "BEE decodeIndices String $n" -pre { + set str [bee::encodeString [string repeat X $n]] + } -body { + bee::decodeIndices $str + } -post { + unset str + } +} + +# ### ### ### ######### ######### ######### ########################### +## Complete diff --git a/tcllib/modules/bee/bee.man b/tcllib/modules/bee/bee.man new file mode 100644 index 0000000..c6c4781 --- /dev/null +++ b/tcllib/modules/bee/bee.man @@ -0,0 +1,343 @@ +[comment {-*- tcl -*- doctools manpage}] +[manpage_begin bee n 0.1] +[keywords bee] +[keywords BitTorrent] +[keywords bittorrent] +[keywords serialization] +[keywords torrent] +[copyright {2004 Andreas Kupries <andreas_kupries@users.sourceforge.net>}] +[moddesc {BitTorrent}] +[titledesc {BitTorrent Serialization Format Encoder/Decoder}] +[category Networking] +[require Tcl 8.4] +[require bee [opt 0.1]] +[description] +[para] + +The [package bee] package provides de- and encoder commands for data +in bencoding (speak 'bee'), the serialization format for data and +messages used by the BitTorrent application. + +[para] + +[section {PUBLIC API}] + +[subsection ENCODER] + +The package provides one encoder command for each of the basic forms, +and two commands per container, one taking a proper tcl data structure +to encode in the container, the other taking the same information as +several arguments. + +[para] +[list_begin definitions] + +[call [cmd ::bee::encodeString] [arg string]] + +Returns the bee-encoding of the [arg string]. + +[call [cmd ::bee::encodeNumber] [arg integer]] + +Returns the bee-encoding of the [arg integer] number. + +[call [cmd ::bee::encodeListArgs] [arg value]...] + +Takes zero or more bee-encoded values and returns the bee-encoding of +their list. + +[call [cmd ::bee::encodeList] [arg list]] + +Takes a list of bee-encoded values and returns the bee-encoding of the +list. + +[call [cmd ::bee::encodeDictArgs] [arg key] [arg value]...] + +Takes zero or more pairs of keys and values and returns the +bee-encoding of the dictionary they form. The values are expected to +be already bee-encoded, but the keys must not be. Their encoding will +be done by the command itself. + +[call [cmd ::bee::encodeDict] [arg dict]] + +Takes a dictionary list of string keys and bee-encoded values and +returns the bee-encoding of the list. Note that the keys in the input +must not be bee-encoded already. This will be done by the command +itself. + +[list_end] +[para] + +[subsection DECODER] + +The package provides two main decoder commands, one for decoding a +string expected to contain a complete data structure, the other for +the incremental decoding of bee-values arriving on a channel. The +latter command is asynchronous and provides the completed decoded +values to the user through a command callback. + +[para] +[list_begin definitions] + +[call [cmd ::bee::decode] [arg string] [opt [arg endvar]] [opt [arg start]]] + +Takes the bee-encoding in the string and returns one decoded value. In +the case of this being a container all contained values are decoded +recursively as well and the result is a properly nested tcl list +and/or dictionary. + +[para] + +If the optional [arg endvar] is set then it is the name of a variable +to store the index of the first character [emph after] the decoded +value into. In other words, if the string contains more than one value +then [arg endvar] can be used to obtain the position of the bee-value +after the bee-value currently decoded. together with [arg start], see +below, it is possible to iterate over the string to extract all +contained values. + +[para] + +The optional [arg start] index defaults to [const 0], i.e. the +beginning of the string. It is the index of the first character of the +bee-encoded value to extract. + +[call [cmd ::bee::decodeIndices] [arg string] [opt [arg endvar]] [opt [arg start]]] + +Takes the same arguments as [cmd ::bee::decode] and returns the same +information in [arg endvar]. The result however is different. Instead +of the tcl value contained in the [arg string] it returns a list +describing the value with respect to type and location (indices for +the first and last character of the bee-value). In case of a container +the structure also contains the same information for all the embedded +values. + +[para] + +Formally the results for the various types of bee-values are: + +[list_begin definitions] +[def string] + +A list containing three elements: + +[list_begin itemized] +[item] +The constant string [const string], denoting the type of the value. + +[item] +An integer number greater than or equal to zero. This is the index of +the first character of the bee-value in the input [arg string]. + +[item] +An integer number greater than or equal to zero. This is the index of +the last character of the bee-value in the input [arg string]. + +[list_end] +[para] + +[emph Note] that this information is present in the results for all +four types of bee-values, with only the first element changing +according to the type of the value. + +[def integer] + +The result is like for strings, except that the type element contains +the constant string [const integer]. + +[def list] + +The result is like before, with two exceptions: One, the type element +contains the constant string [const list]. And two, the result +actually contains four elements. The last element is new, and contains +the index data as described here for all elements of the bee-list. + +[def dictionary] + +The result is like for strings, except that the type element contains +the constant string [const dict]. A fourth element is present as well, +with a slightly different structure than for lists. The element is a +dictionary mapping from the strings keys of the bee-dictionary to a +list containing two elements. The first of them is the index +information for the key, and the second element is the index +information for the value the key maps to. This structure is the only +which contains not only index data, but actual values from the +bee-string. While the index information of the keys is unique enough, +i.e. serviceable as keys, they are not easy to navigate when trying to +find particular element. Using the actual keys makes this much easier. + +[list_end] +[para] + +[call [cmd ::bee::decodeChannel] [arg chan] \ + [option -command] [arg cmdprefix] \ + [opt [option -exact]] \ + [opt "[option -prefix] [arg data]"] \ +] + +The command creates a decoder for a series of bee-values arriving on +the channel [arg chan] and returns its handle. This handle can be used +to remove the decoder again. + +Setting up another bee decoder on [arg chan] while a bee decoder is +still active will fail with an error message. + +[para] +[list_begin definitions] +[def [option -command]] + +The command prefix [arg cmdprefix] specified by the [emph required] +option [option -command] is used to report extracted values and +exceptional situations (error, and EOF on the channel). + +The callback will be executed at the global level of the interpreter, +with two or three arguments. The exact call signatures are + +[para] +[list_begin definitions] +[call [cmd cmdprefix] [method eof] [arg token]] + +The decoder has reached eof on the channel [arg chan]. No further +invocations of the callback will be made after this. The channel has +already been closed at the time of the call, and the [arg token] is +not valid anymore as well. + +[call [cmd cmdprefix] [method error] [arg token] [arg message]] + +The decoder encountered an error, which is not eof. For example a +malformed bee-value. The [arg message] provides details about the +error. The decoder token is in the same state as for eof, +i.e. invalid. The channel however is kept open. + +[call [cmd cmdprefix] [method value] [arg token] [arg value]] + +The decoder received and successfully decoded a bee-value. + +The format of the equivalent tcl [arg value] is the same as returned +by [cmd ::bee::decode]. The channel is still open and the decoder +token is valid. This means that the callback is able to remove the +decoder. + +[list_end] +[para] + +[def [option -exact]] + +By default the decoder assumes that the remainder of the data in the +channel consists only of bee-values, and reads as much as possible per +event, without regard for boundaries between bee-values. This means +that if the the input contains non-bee data after a series of +bee-value the beginning of that data may be lost because it was +already read by the decoder, but not processed. + +[para] + +The [option -exact] was made for this situation. When specified the +decoder will take care to not read any characters behind the currently +processed bee-value, so that any non-bee data is kept in the channel +for further processing after removal of the decoder. + +[para] + +[def [option -prefix]] + +If this option is specified its value is assumed to be the beginning +of the bee-value and used to initialize the internal decoder +buffer. This feature is required if the creator of the decoder used +data from the channel to determine if it should create the decoder or +not. Without the option this data would be lost to the decoding. + +[list_end] +[para] + +[call [cmd ::bee::decodeCancel] [arg token]] + +This command cancels the decoder set up by [cmd ::bee::decodeChannel] +and represented by the handle [arg token]. + +[call [cmd ::bee::decodePush] [arg token] [arg string]] + +This command appends the [arg string] to the internal decoder +buffer. It is the runtime equivalent of the option [option -prefix] of +[cmd ::bee::decodeChannel]. Use it to push data back into the decoder +when the [method value] callback used data from the channel to +determine if it should decode another bee-value or not. + +[list_end] +[para] + +[section {FORMAT DEFINITION}] + +Data in the bee serialization format is constructed from two basic +forms, and two container forms. The basic forms are strings and +integer numbers, and the containers are lists and dictionaries. + +[para] +[list_begin definitions] +[def "String [arg S]"] + +A string [arg S] of length [arg L] is encoded by the string + +"[arg L][const :][arg S]", where the length is written out in textual +form. + +[def "Integer [arg N]"] + +An integer number [arg N] is encoded by the string + +"[const i][arg N][const e]". + +[def "List [arg v1] ... [arg vn]"] + +A list of the values [arg v1] to [arg vn] is encoded by the string + +"[const l][arg BV1]...[arg BVn][const e]" + +where "BV[var i]" is the bee-encoding of the value "v[var i]". + +[def "Dict [arg k1] -> [arg v1] ..."] + +A dictionary mapping the string key [arg k][var i] to the value + +[arg v][var i], for [var i] in [const 1] ... [var n] +is encoded by the string + +"[const d][arg BK][var i][arg BV][ var i]...[const e]" + +for i in [const 1] ... [var n], where "BK[var i]" is the bee-encoding +of the key string "k[var i]". and "BV[var i]" is the bee-encoding of +the value "v[var i]". + +[para] + +[emph Note]: The bee-encoding does not retain the order of the keys in +the input, but stores in a sorted order. The sorting is done for the +"raw strings". + +[list_end] +[para] + +Note that the type of each encoded item can be determined immediately +from the first character of its representation: + +[para] +[list_begin definitions] +[def i] +Integer. +[def l] +List. +[def d] +Dictionary. +[def "[lb]0-9[rb]"] +String. +[list_end] +[para] + +By wrapping an integer number into [const i]...[const e] the format +makes sure that they are different from strings, which all begin with +a digit. + +[section EXAMPLES] + +[vset CATEGORY bee] +[include ../doctools2base/include/feedback.inc] +[manpage_end] diff --git a/tcllib/modules/bee/bee.pcx b/tcllib/modules/bee/bee.pcx new file mode 100644 index 0000000..b8cf178 --- /dev/null +++ b/tcllib/modules/bee/bee.pcx @@ -0,0 +1,81 @@ +# -*- tcl -*- bee.pcx +# Syntax of the commands provided by package bee. +# +# For use by TclDevKit's static syntax checker (v4.1+). +# See http://www.activestate.com/solutions/tcl/ +# See http://aspn.activestate.com/ASPN/docs/Tcl_Dev_Kit/4.0/Checker.html#pcx_api +# for the specification of the format of the code in this file. +# + +package require pcx +pcx::register bee +pcx::tcldep 0.1 needs tcl 8.4 + +namespace eval ::bee {} + +pcx::message needCommand {Required -command is missing} err + +pcx::check 0.1 std ::bee::decode \ + {checkSimpleArgs 1 3 { + checkWord + checkVarNameWrite + checkWholeNum + }} +pcx::check 0.1 std ::bee::decodeCancel \ + {checkSimpleArgs 1 1 { + checkWord + }} +pcx::check 0.1 std ::bee::decodeChannel \ + {checkSimpleArgs 1 -1 { + checkChannelID + {checkConstrained {checkSequence { + {checkSwitches exact { + {-command {checkSetConstraint cmd {checkProcCall 3}}} + {-exaxt} + {-prefix checkWord} + } {checkAtEnd}} + {checkConstraint { + {!cmd {warn bee::needCommand {} checkNOP}} + } {checkNOP}} + }}} + }} +pcx::check 0.1 std ::bee::decodeIndices \ + {checkSimpleArgs 1 3 { + checkWord + checkVarNameWrite + checkWholeNum + }} +pcx::check 0.1 std ::bee::decodePush \ + {checkSimpleArgs 2 2 { + checkWord + checkWord + }} +pcx::check 0.1 std ::bee::encodeDict \ + {checkSimpleArgs 1 1 { + checkDict + }} +pcx::check 0.1 std ::bee::encodeDictArgs \ + {checkSimpleArgsModNk 0 2 { + checkWord + checkWord + }} +pcx::check 0.1 std ::bee::encodeList \ + {checkSimpleArgs 1 1 { + checkList + }} +pcx::check 0.1 std ::bee::encodeListArgs \ + {checkSimpleArgs 0 -1 { + checkWord + }} +pcx::check 0.1 std ::bee::encodeNumber \ + {checkSimpleArgs 1 1 { + checkInt + }} +pcx::check 0.1 std ::bee::encodeString \ + {checkSimpleArgs 1 1 { + checkWord + }} + +# Initialization via pcx::init. +# Use a ::bee::init procedure for non-standard initialization. +pcx::complete diff --git a/tcllib/modules/bee/bee.tcl b/tcllib/modules/bee/bee.tcl new file mode 100644 index 0000000..6eb53c0 --- /dev/null +++ b/tcllib/modules/bee/bee.tcl @@ -0,0 +1,990 @@ +# bee.tcl -- +# +# BitTorrent Bee de- and encoder. +# +# Copyright (c) 2004 by Andreas Kupries <andreas_kupries@users.sourceforge.net> +# See the file license.terms. + +package require Tcl 8.4 + +namespace eval ::bee { + # Encoder commands + namespace export \ + encodeString encodeNumber \ + encodeListArgs encodeList \ + encodeDictArgs encodeDict + + # Decoder commands. + namespace export \ + decode \ + decodeChannel \ + decodeCancel \ + decodePush + + # Channel decoders, reference to state information, keyed by + # channel handle. + + variable bee + array set bee {} + + # Counter for generation of names for the state variables. + + variable count 0 + + # State information for the channel decoders. + + # stateN, with N an integer number counting from 0 on up. + # ...(chan) Handle of channel the decoder is for. + # ...(cmd) Command prefix, completion callback + # ...(exact) Boolean flag, set for exact processing. + # ...(read) Buffer for new characters to process. + # ...(type) Type of current value (integer, string, list, dict) + # ...(value) Buffer for assembling the current value. + # ...(pend) Stack of pending 'value' buffers, for nested + # containers. + # ...(state) Current state of the decoding state machine. + + # States of the finite automaton ... + # intro - One char, type of value, or 'e' as stop of container. + # signum - sign or digit, for integer. + # idigit - digit, for integer, or 'e' as stop + # ldigit - digit, for length of string, or : + # data - string data, 'get' characters. + # Containers via 'pend'. + + #Debugging help, nesting level + #variable X 0 +} + + +# ::bee::encodeString -- +# +# Encode a string to bee-format. +# +# Arguments: +# string The string to encode. +# +# Results: +# The bee-encoded form of the string. + +proc ::bee::encodeString {string} { + return "[string length $string]:$string" +} + + +# ::bee::encodeNumber -- +# +# Encode an integer number to bee-format. +# +# Arguments: +# num The integer number to encode. +# +# Results: +# The bee-encoded form of the integer number. + +proc ::bee::encodeNumber {num} { + if {![string is integer -strict $num]} { + return -code error "Expected integer number, got \"$num\"" + } + + # The reformatting deals with hex, octal and other tcl + # representation of the value. In other words we normalize the + # string representation of the input value. + + set num [format %d $num] + return "i${num}e" +} + + +# ::bee::encodeList -- +# +# Encode a list of bee-coded values to bee-format. +# +# Arguments: +# list The list to encode. +# +# Results: +# The bee-encoded form of the list. + +proc ::bee::encodeList {list} { + return "l[join $list ""]e" +} + + +# ::bee::encodeListArgs -- +# +# Encode a variable list of bee-coded values to bee-format. +# +# Arguments: +# args The values to encode. +# +# Results: +# The bee-encoded form of the list of values. + +proc ::bee::encodeListArgs {args} { + return [encodeList $args] +} + + +# ::bee::encodeDict -- +# +# Encode a dictionary of keys and bee-coded values to bee-format. +# +# Arguments: +# dict The dictionary to encode. +# +# Results: +# The bee-encoded form of the dictionary. + +proc ::bee::encodeDict {dict} { + if {([llength $dict] % 2) == 1} { + return -code error "Expected even number of elements, got \"[llength $dict]\"" + } + set temp [list] + foreach {k v} $dict { + lappend temp [list $k $v] + } + set res "d" + foreach item [lsort -index 0 $temp] { + foreach {k v} $item break + append res [encodeString $k]$v + } + append res "e" + return $res +} + + +# ::bee::encodeDictArgs -- +# +# Encode a variable dictionary of keys and bee-coded values to bee-format. +# +# Arguments: +# args The keys and values to encode. +# +# Results: +# The bee-encoded form of the dictionary. + +proc ::bee::encodeDictArgs {args} { + return [encodeDict $args] +} + + +# ::bee::decode -- +# +# Decode a bee-encoded value and returns the embedded tcl +# value. For containers this recurses into the contained value. +# +# Arguments: +# value The string containing the bee-encoded value to decode. +# evar Optional. If set the name of the variable to store the +# index of the first character after the decoded value to. +# start Optional. If set the index of the first character of the +# value to decode. Defaults to 0, i.e. the beginning of the +# string. +# +# Results: +# The tcl value embedded in the encoded string. + +proc ::bee::decode {value {evar {}} {start 0}} { + #variable X + #puts -nonewline "[string repeat " " $X]decode @$start" ; flush stdout + + if {$evar ne ""} {upvar 1 $evar end} else {set end _} + + if {[string length $value] < ($start+2)} { + # This checked that the 'start' index is still in the string, + # and the end of the value most likely as well. Note that each + # encoded value consists of at least two characters (the + # bracketing characters for integer, list, and dict, and for + # string at least one digit length and the colon). + + #puts \t[string length $value]\ <\ ($start+2) + return -code error "String not large enough for value" + } + + set type [string index $value $start] + + #puts -nonewline " $type=" ; flush stdout + + if {$type eq "i"} { + # Extract integer + #puts -nonewline integer ; flush stdout + + incr start ; # Skip over intro 'i'. + set end [string first e $value $start] + if {$end < 0} { + return -code error "End of integer number not found" + } + incr end -1 ; # Get last character before closing 'e'. + set num [string range $value $start $end] + if { + [regexp {^-0+$} $num] || + ![string is integer -strict $num] || + (([string length $num] > 1) && [string match 0* $num]) + } { + return -code error "Expected integer number, got \"$num\"" + } + incr end 2 ; # Step after closing 'e' to the beginning of + # ........ ; # the next bee-value behind the current one. + + #puts " ($num) @$end" + return $num + + } elseif {($type eq "l") || ($type eq "d")} { + #puts -nonewline $type\n ; flush stdout + + # Extract list or dictionary, recursively each contained + # element. From the perspective of the decoder this is the + # same, the tcl representation of both is a list, and for a + # dictionary keys and values are also already in the correct + # order. + + set result [list] + incr start ; # Step over intro 'e' to beginning of the first + # ........ ; # contained value, or behind the container (if + # ........ ; # empty). + + set end $start + #incr X + while {[string index $value $start] ne "e"} { + lappend result [decode $value end $start] + set start $end + } + #incr X -1 + incr end + + #puts "[string repeat " " $X]($result) @$end" + + if {$type eq "d" && ([llength $result] % 2 == 1)} { + return -code error "Dictionary has to be of even length" + } + return $result + + } elseif {[string match {[0-9]} $type]} { + #puts -nonewline string ; flush stdout + + # Extract string. First the length, bounded by a colon, then + # the appropriate number of characters. + + set end [string first : $value $start] + if {$end < 0} { + return -code error "End of string length not found" + } + incr end -1 + set length [string range $value $start $end] + incr end 2 ;# Skip to beginning of the string after the colon + + if {![string is integer -strict $length]} { + return -code error "Expected integer number for string length, got \"$length\"" + } elseif {$length < 0} { + # This cannot happen. To happen "-" has to be first character, + # and this is caught as unknown bee-type. + return -code error "Illegal negative string length" + } elseif {($end + $length) > [string length $value]} { + return -code error "String not large enough for value" + } + + #puts -nonewline \[$length\] ; flush stdout + if {$length > 0} { + set start $end + incr end $length + incr end -1 + set result [string range $value $start $end] + incr end + } else { + set result "" + } + + #puts " ($result) @$end" + return $result + + } else { + return -code error "Unknown bee-type \"$type\"" + } +} + +# ::bee::decodeIndices -- +# +# Similar to 'decode', but does not return the decoded tcl values, +# but a structure containing the start- and end-indices for all +# values in the structure. +# +# Arguments: +# value The string containing the bee-encoded value to decode. +# evar Optional. If set the name of the variable to store the +# index of the first character after the decoded value to. +# start Optional. If set the index of the first character of the +# value to decode. Defaults to 0, i.e. the beginning of the +# string. +# +# Results: +# The structure of the value, with indices and types for all +# contained elements. + +proc ::bee::decodeIndices {value {evar {}} {start 0}} { + #variable X + #puts -nonewline "[string repeat " " $X]decode @$start" ; flush stdout + + if {$evar ne ""} {upvar 1 $evar end} else {set end _} + + if {[string length $value] < ($start+2)} { + # This checked that the 'start' index is still in the string, + # and the end of the value most likely as well. Note that each + # encoded value consists of at least two characters (the + # bracketing characters for integer, list, and dict, and for + # string at least one digit length and the colon). + + #puts \t[string length $value]\ <\ ($start+2) + return -code error "String not large enough for value" + } + + set type [string index $value $start] + + #puts -nonewline " $type=" ; flush stdout + + if {$type eq "i"} { + # Extract integer + #puts -nonewline integer ; flush stdout + + set begin $start + + incr start ; # Skip over intro 'i'. + set end [string first e $value $start] + if {$end < 0} { + return -code error "End of integer number not found" + } + incr end -1 ; # Get last character before closing 'e'. + set num [string range $value $start $end] + if { + [regexp {^-0+$} $num] || + ![string is integer -strict $num] || + (([string length $num] > 1) && [string match 0* $num]) + } { + return -code error "Expected integer number, got \"$num\"" + } + incr end + set stop $end + incr end 1 ; # Step after closing 'e' to the beginning of + # ........ ; # the next bee-value behind the current one. + + #puts " ($num) @$end" + return [list integer $begin $stop] + + } elseif {$type eq "l"} { + #puts -nonewline $type\n ; flush stdout + + # Extract list, recursively each contained element. + + set result [list] + + lappend result list $start @ + + incr start ; # Step over intro 'e' to beginning of the first + # ........ ; # contained value, or behind the container (if + # ........ ; # empty). + + set end $start + #incr X + + set contained [list] + while {[string index $value $start] ne "e"} { + lappend contained [decodeIndices $value end $start] + set start $end + } + lappend result $contained + #incr X -1 + set stop $end + incr end + + #puts "[string repeat " " $X]($result) @$end" + + return [lreplace $result 2 2 $stop] + + } elseif {($type eq "l") || ($type eq "d")} { + #puts -nonewline $type\n ; flush stdout + + # Extract dictionary, recursively each contained element. + + set result [list] + + lappend result dict $start @ + + incr start ; # Step over intro 'e' to beginning of the first + # ........ ; # contained value, or behind the container (if + # ........ ; # empty). + + set end $start + set atkey 1 + #incr X + + set contained [list] + set val [list] + while {[string index $value $start] ne "e"} { + if {$atkey} { + lappend contained [decode $value {} $start] + lappend val [decodeIndices $value end $start] + set atkey 0 + } else { + lappend val [decodeIndices $value end $start] + lappend contained $val + set val [list] + set atkey 1 + } + set start $end + } + lappend result $contained + #incr X -1 + set stop $end + incr end + + #puts "[string repeat " " $X]($result) @$end" + + if {[llength $result] % 2 == 1} { + return -code error "Dictionary has to be of even length" + } + return [lreplace $result 2 2 $stop] + + } elseif {[string match {[0-9]} $type]} { + #puts -nonewline string ; flush stdout + + # Extract string. First the length, bounded by a colon, then + # the appropriate number of characters. + + set end [string first : $value $start] + if {$end < 0} { + return -code error "End of string length not found" + } + incr end -1 + set length [string range $value $start $end] + incr end 2 ;# Skip to beginning of the string after the colon + + if {![string is integer -strict $length]} { + return -code error "Expected integer number for string length, got \"$length\"" + } elseif {$length < 0} { + # This cannot happen. To happen "-" has to be first character, + # and this is caught as unknown bee-type. + return -code error "Illegal negative string length" + } elseif {($end + $length) > [string length $value]} { + return -code error "String not large enough for value" + } + + #puts -nonewline \[$length\] ; flush stdout + incr end -1 + if {$length > 0} { + incr end $length + set stop $end + } else { + set stop $end + } + incr end + + #puts " ($result) @$end" + return [list string $start $stop] + + } else { + return -code error "Unknown bee-type \"$type\"" + } +} + + +# ::bee::decodeChannel -- +# +# Attach decoder for a bee-value to a channel. See the +# documentation for details. +# +# Arguments: +# chan Channel to attach to. +# -command cmdprefix Completion callback. Required. +# -exact Keep running after completion. +# -prefix data Seed for decode buffer. +# +# Results: +# A token to use when referring to the decoder. +# For example when canceling it. + +proc ::bee::decodeChannel {chan args} { + variable bee + if {[info exists bee($chan)]} { + return -code error "bee-Decoder already active for channel" + } + + # Create state and token. + + variable count + variable [set st state$count] + array set $st {} + set bee($chan) $st + upvar 0 $st state + incr count + + # Initialize the decoder state, process the options. When + # encountering errors here destroy the half-baked state before + # throwing the message. + + set state(chan) $chan + array set state { + exact 0 + type ? + read {} + value {} + pend {} + state intro + get 1 + } + + while {[llength $args]} { + set option [lindex $args 0] + set args [lrange $args 1 end] + if {$option eq "-command"} { + if {![llength $args]} { + unset bee($chan) + unset state + return -code error "Missing value for option -command." + } + set state(cmd) [lindex $args 0] + set args [lrange $args 1 end] + + } elseif {$option eq "-prefix"} { + if {![llength $args]} { + unset bee($chan) + unset state + return -code error "Missing value for option -prefix." + } + set state(read) [lindex $args 0] + set args [lrange $args 1 end] + + } elseif {$option eq "-exact"} { + set state(exact) 1 + } else { + unset bee($chan) + unset state + return -code error "Illegal option \"$option\",\ + expected \"-command\", \"-prefix\", or \"-keep\"" + } + } + + if {![info exists state(cmd)]} { + unset bee($chan) + unset state + return -code error "Missing required completion callback." + } + + # Set up the processing of incoming data. + + fileevent $chan readable [list ::bee::Process $chan $bee($chan)] + + # Return the name of the state array as token. + return $bee($chan) +} + +# ::bee::Parse -- +# +# Internal helper. Fileevent handler for a decoder. +# Parses input and handles both error and eof conditions. +# +# Arguments: +# token The decoder to run on its input. +# +# Results: +# None. + +proc ::bee::Process {chan token} { + if {[catch {Parse $token} msg]} { + # Something failed. Destroy and report. + Command $token error $msg + return + } + + if {[eof $chan]} { + # Having data waiting, either in the input queue, or in the + # output stack (of nested containers) is a failure. Report + # this instead of the eof. + + variable $token + upvar 0 $token state + + if { + [string length $state(read)] || + [llength $state(pend)] || + [string length $state(value)] || + ($state(state) ne "intro") + } { + Command $token error "Incomplete value at end of channel" + } else { + Command $token eof + } + } + return +} + +# ::bee::Parse -- +# +# Internal helper. Reading from the channel and parsing the input. +# Uses a hardwired state machine. +# +# Arguments: +# token The decoder to run on its input. +# +# Results: +# None. + +proc ::bee::Parse {token} { + variable $token + upvar 0 $token state + upvar 0 state(state) current + upvar 0 state(read) input + upvar 0 state(type) type + upvar 0 state(value) value + upvar 0 state(pend) pend + upvar 0 state(exact) exact + upvar 0 state(get) get + set chan $state(chan) + + #puts Parse/$current + + if {!$exact} { + # Add all waiting characters to the buffer so that we can process as + # much as is possible in one go. + append input [read $chan] + } else { + # Exact reading. Usually one character, but when in the data + # section for a string value we know for how many characters + # we are looking for. + + append input [read $chan $get] + } + + # We got nothing, do nothing. + if {![string length $input]} return + + + if {$current eq "data"} { + # String data, this can be done faster, as we read longer + # sequences of characters for this. + set l [string length $input] + if {$l < $get} { + # Not enough, wait for more. + append value $input + incr get -$l + return + } elseif {$l == $get} { + # Got all, exactly. Prepare state machine for next value. + + if {[Complete $token $value$input]} return + + set current intro + set get 1 + set value "" + set input "" + + return + } else { + # Got more than required (only for !exact). + + incr get -1 + if {[Complete $token $value[string range $input 0 $get]]} {return} + + incr get + set input [string range $input $get end] + set get 1 + set value "" + set current intro + # This now falls into the loop below. + } + } + + set where 0 + set n [string length $input] + + #puts Parse/$n + + while {$where < $n} { + # Hardwired state machine. Get current character. + set ch [string index $input $where] + + #puts Parse/@$where/$current/$ch/ + if {$current eq "intro"} { + # First character of a value. + + if {$ch eq "i"} { + # Begin reading integer. + set type integer + set current signum + } elseif {$ch eq "l"} { + # Begin a list. + set type list + lappend pend list {} + #set current intro + + } elseif {$ch eq "d"} { + # Begin a dictionary. + set type dict + lappend pend dict {} + #set current intro + + } elseif {$ch eq "e"} { + # Close a container. Throw an error if there is no + # container to close. + + if {![llength $pend]} { + return -code error "End of container outside of container." + } + + set v [lindex $pend end] + set t [lindex $pend end-1] + set pend [lrange $pend 0 end-2] + + if {$t eq "dict" && ([llength $v] % 2 == 1)} { + return -code error "Dictionary has to be of even length" + } + + if {[Complete $token $v]} {return} + set current intro + + } elseif {[string match {[0-9]} $ch]} { + # Begin reading a string, length section first. + set type string + set current ldigit + set value $ch + + } else { + # Unknown type. Throw error. + return -code error "Unknown bee-type \"$ch\"" + } + + # To next character. + incr where + } elseif {$current eq "signum"} { + # Integer number, a minus sign, or a digit. + if {[string match {[-0-9]} $ch]} { + append value $ch + set current idigit + } else { + return -code error "Syntax error in integer,\ + expected sign or digit, got \"$ch\"" + } + incr where + + } elseif {$current eq "idigit"} { + # Integer number, digit or closing 'e'. + + if {[string match {[-0-9]} $ch]} { + append value $ch + } elseif {$ch eq "e"} { + # Integer closes. Validate and report. + #puts validate + if { + [regexp {^-0+$} $value] || + ![string is integer -strict $value] || + (([string length $value] > 1) && [string match 0* $value]) + } { + return -code error "Expected integer number, got \"$value\"" + } + + if {[Complete $token $value]} {return} + set value "" + set current intro + } else { + return -code error "Syntax error in integer,\ + expected digit, or 'e', got \"$ch\"" + } + incr where + + } elseif {$current eq "ldigit"} { + # String, length section, digit, or : + + if {[string match {[-0-9]} $ch]} { + append value $ch + + } elseif {$ch eq ":"} { + # Length section closes, validate, + # then perform data processing. + + set num $value + if { + [regexp {^-0+$} $num] || + ![string is integer -strict $num] || + (([string length $num] > 1) && [string match 0* $num]) + } { + return -code error "Expected integer number as string length, got \"$num\"" + } + + set value "" + + # We may have already part of the data in + # memory. Process that piece before looking for more. + + incr where + set have [expr {$n - $where}] + if {$num < $have} { + # More than enough in the buffer. + + set end $where + incr end $num + incr end -1 + + if {[Complete $token [string range $input $where $end]]} {return} + + set where $end ;# Further processing behind the string. + set current intro + + } elseif {$num == $have} { + # Just enough. + + if {[Complete $token [string range $input $where end]]} {return} + + set where $n + set current intro + } else { + # Not enough. Initialize value with the data we + # have (after the colon) and stop processing for + # now. + + set value [string range $input $where end] + set current data + set get $num + set input "" + return + } + } else { + return -code error "Syntax error in string length,\ + expected digit, or ':', got \"$ch\"" + } + incr where + } else { + # unknown state = internal error + return -code error "Unknown decoder state \"$current\", internal error" + } + } + + set input "" + return +} + +# ::bee::Command -- +# +# Internal helper. Runs the decoder command callback. +# +# Arguments: +# token The decoder invoking its callback +# how Which method to invoke (value, error, eof) +# args Arguments for the method. +# +# Results: +# A boolean flag. Set if further processing has to stop. + +proc ::bee::Command {token how args} { + variable $token + upvar 0 $token state + + #puts Report/$token/$how/$args/ + + set cmd $state(cmd) + set chan $state(chan) + + # We catch the fileevents because they will fail when this is + # called from the 'Close'. The channel will already be gone in + # that case. + + set stop 0 + if {($how eq "error") || ($how eq "eof")} { + variable bee + + set stop 1 + fileevent $chan readable {} + unset bee($chan) + unset state + + if {$how eq "eof"} { + #puts \tclosing/$chan + close $chan + } + } + + lappend cmd $how $token + foreach a $args {lappend cmd $a} + uplevel #0 $cmd + + if {![info exists state]} { + # The decoder token was killed by the callback, stop + # processing. + set stop 1 + } + + #puts /$stop/[file channels] + return $stop +} + +# ::bee::Complete -- +# +# Internal helper. Reports a completed value. +# +# Arguments: +# token The decoder reporting the value. +# value The value to report. +# +# Results: +# A boolean flag. Set if further processing has to stop. + +proc ::bee::Complete {token value} { + variable $token + upvar 0 $token state + upvar 0 state(pend) pend + + if {[llength $pend]} { + # The value is part of a container. Add the value to its end + # and keep processing. + + set pend [lreplace $pend end end \ + [linsert [lindex $pend end] end \ + $value]] + + # Don't stop. + return 0 + } + + # The value is at the top, report it. The callback determines if + # we keep processing. + + return [Command $token value $value] +} + +# ::bee::decodeCancel -- +# +# Destroys the decoder referenced by the token. +# +# Arguments: +# token The decoder to destroy. +# +# Results: +# None. + +proc ::bee::decodeCancel {token} { + variable bee + variable $token + upvar 0 $token state + unset bee($state(chan)) + unset state + return +} + +# ::bee::decodePush -- +# +# Push data into the decoder input buffer. +# +# Arguments: +# token The decoder to extend. +# string The characters to add. +# +# Results: +# None. + +proc ::bee::decodePush {token string} { + variable $token + upvar 0 $token state + append state(read) $string + return +} + + +package provide bee 0.1 diff --git a/tcllib/modules/bee/bee.test b/tcllib/modules/bee/bee.test new file mode 100644 index 0000000..4ea1c7c --- /dev/null +++ b/tcllib/modules/bee/bee.test @@ -0,0 +1,384 @@ +# -*- tcl -*- +# bee.test: tests for the bee encoding. +# +# Copyright (c) 2004 by Andreas Kupries <andreas_kupries@users.sourceforge.net> +# All rights reserved. +# +# RCS: @(#) $Id: bee.test,v 1.9 2006/10/09 21:41:39 andreas_kupries Exp $ + +# ------------------------------------------------------------------------- + +source [file join \ + [file dirname [file dirname [file join [pwd] [info script]]]] \ + devtools testutilities.tcl] + +testsNeedTcl 8.4 +testsNeedTcltest 1.0 + +testing { + useLocal bee.tcl bee +} + +# ------------------------------------------------------------------------- +# encoder ............................................................ + +test bee-1.0 {encoder, string} { + bee::encodeString "" +} {0:} + +test bee-1.1 {encoder, string} { + bee::encodeString spam +} {4:spam} + +test bee-1.2 {encoder, string, wrong#args} { + catch {bee::encodeString} msg + set msg +} [tcltest::wrongNumArgs {bee::encodeString} {string} 1] + +test bee-1.3 {encoder, string, wrong#args} { + catch {bee::encodeString 3 4} msg + set msg +} [tcltest::tooManyArgs {bee::encodeString} {string}] + + + +test bee-2.0 {encoder, integer} { + bee::encodeNumber 0 +} {i0e} + +test bee-2.1 {encoder, integer, stupid zero} { + bee::encodeNumber -0 +} {i0e} + +test bee-2.2 {encoder, integer, good octal} { + bee::encodeNumber 004 +} {i4e} + +test bee-2.3 {encoder, integer, negatives} { + bee::encodeNumber -5 +} {i-5e} + +test bee-2.4 {encoder, integer, non-numeric} { + catch {bee::encodeNumber spam} msg + set msg +} {Expected integer number, got "spam"} + +test bee-2.5 {encoder, integer, bad octal} { + catch {bee::encodeNumber 009} msg + set msg +} {Expected integer number, got "009"} + +test bee-2.6 {encoder, integer, hex} { + bee::encodeNumber 0x45 +} {i69e} + +test bee-2.7 {encoder, integer, wrong#args} { + catch {bee::encodeNumber} msg + set msg +} [tcltest::wrongNumArgs {bee::encodeNumber} {num} 1] + +test bee-2.8 {encoder, integer, wrong#args} { + catch {bee::encodeNumber 3 4} msg + set msg +} [tcltest::tooManyArgs {bee::encodeNumber} {num}] + + + +test bee-3.0 {encoder, list, empty} { + bee::encodeListArgs +} {le} + +test bee-3.1 {encoder, list, empty elements} { + bee::encodeListArgs [bee::encodeString {}] [bee::encodeString {}] +} {l0:0:e} + +test bee-3.2 {encoder, list, regular elements} { + bee::encodeListArgs [bee::encodeString eggs] [bee::encodeNumber 12] +} {l4:eggsi12ee} + +test bee-3.3 {encoder, list, empty} { + bee::encodeList {} +} {le} + +test bee-3.4 {encoder, list, empty elements} { + bee::encodeList [list [bee::encodeString {}] [bee::encodeString {}]] +} {l0:0:e} + +test bee-3.5 {encoder, list, regular elements} { + bee::encodeList [list [bee::encodeString eggs] [bee::encodeNumber 12]] +} {l4:eggsi12ee} + +test bee-3.6 {encoder, list, empty} { + catch {bee::encodeList} msg + set msg +} [tcltest::wrongNumArgs {bee::encodeList} {list} 1] + +test bee-3.7 {encoder, list, empty} { + catch {bee::encodeList 1 2} msg + set msg +} [tcltest::tooManyArgs {bee::encodeList} {list}] + + +test bee-4.0 {encoder, dict, empty} { + bee::encodeDictArgs +} {de} + +test bee-4.1 {encoder, dict, empty elements} { + bee::encodeDictArgs {} [bee::encodeString {}] +} {d0:0:e} + +test bee-4.2 {encoder, dict, regular elements} { + bee::encodeDictArgs eggs [bee::encodeNumber 12] +} {d4:eggsi12ee} + +test bee-4.3 {encoder, dict, empty} { + bee::encodeDict {} +} {de} + +test bee-4.4 {encoder, dict, empty elements} { + bee::encodeDict [list {} [bee::encodeString {}]] +} {d0:0:e} + +test bee-4.5 {encoder, dict, regular elements} { + bee::encodeDict [list eggs [bee::encodeNumber 12]] +} {d4:eggsi12ee} + +test bee-4.6 {encoder, dict, empty} { + catch {bee::encodeDict} msg + set msg +} [tcltest::wrongNumArgs {bee::encodeDict} {dict} 1] + +test bee-4.7 {encoder, dict, empty} { + catch {bee::encodeDict 1 2} msg + set msg +} [tcltest::tooManyArgs {bee::encodeDict} {dict}] + +test bee-4.8 {encoder, dict, sorted keys} { + bee::encodeDictArgs spam [bee::encodeNumber 2] eggs [bee::encodeNumber 12] +} {d4:eggsi12e4:spami2ee} + + +# decoder ............................................................ + +proc tick {m tok args} { + global res tick + lappend res $m $args + if {$m eq "eof" || $m eq "error"} {set tick 0} + return +} + +proc tickoff {m tok args} { + global res tick + lappend res $m $args + bee::decodeCancel $tok + set tick 0 + return +} + +proc gen {name data} { + set path [makeFile {} $name] + set f [open $path w] + puts -nonewline $f $data + close $f + return $path +} + +foreach {n bee result} { + 0 i0e {0 3} + 1 i-5e {-5 4} + 2 4:spam {spam 6} + 3 0: {{} 2} + 4 le {{} 2} + 5 l0:e {{{}} 4} + 6 li5ee {5 5} + 7 li5e4:spame {{5 spam} 11} + 8 de {{} 2} + 9 d0:0:e {{{} {}} 6} + 10 d0:i5ee {{{} 5} 7} + 11 d1:a4:spame {{a spam} 11} + 12 ld1:a4:spame3:egge {{{a spam} egg} 18} + 13 13:eggs+spam+ham {eggs+spam+ham 16} + 14 d1:a1:b1:c1:de {{a b c d} 14} +} { + test bee-5.$n {decoder} { + list [bee::decode $bee end] $end + } $result ; # {} + + test bee-6.$n {decoder, channel} { + set path [gen bee6.$n $bee] + + set res "" ; set tick 1 + bee::decodeChannel [open $path r] \ + -command tick + vwait tick + removeFile bee6.$n + set res + } [list value [list [lindex $result 0]] eof {}] ; # {} + + test bee-7.$n {decoder, channel} { + set path [gen bee7.$n $bee] + + set res "" ; set tick 1 + bee::decodeChannel [open $path r] \ + -command tick -exact + vwait tick + removeFile bee7.$n + set res + } [list value [list [lindex $result 0]] eof {}] ; # {} +} + +foreach {n bee result resultchan} { + 0 i-0e {Expected integer number, got "-0"} + {Expected integer number, got "-0"} + 1 i-5 {End of integer number not found} + {Incomplete value at end of channel} + 2 ie {Expected integer number, got ""} + {Syntax error in integer, expected sign or digit, got "e"} + 3 4: {String not large enough for value} + {Incomplete value at end of channel} + 4 1: {String not large enough for value} + {Incomplete value at end of channel} + 5 0 {String not large enough for value} + {Incomplete value at end of channel} + 6 123 {End of string length not found} + {Incomplete value at end of channel} + 7 12t: {Expected integer number for string length, got "12t"} + {Syntax error in string length, expected digit, or ':', got "t"} + 8 -123 {Unknown bee-type "-"} + {Unknown bee-type "-"} + 9 d0:e {Dictionary has to be of even length} + {Dictionary has to be of even length} +} { + test bee-8.$n {decoder errors} { + catch {bee::decode $bee} msg + set msg + } $result ; # {} + + test bee-9.$n {decoder, channel} { + set path [gen bee9.$n $bee] + + set res "" ; set tick 1 + bee::decodeChannel [set f [open $path r]] \ + -command tick + vwait tick + close $f + removeFile bee9.$n + set res + } [list error [list $resultchan]] ;# {} + + test bee-10.$n {decoder, channel} { + set path [gen bee10.$n $bee] + + set res "" ; set tick 1 + bee::decodeChannel [set f [open $path r]] \ + -command tick -exact + vwait tick + close $f + removeFile bee10.$n + set res + } [list error [list $resultchan]] ;# {} +} + + +foreach {n bee result} { + 0 i0e {{integer 0 2} 3} + 1 i-5e {{integer 0 3} 4} + 2 4:spam {{string 0 5} 6} + 3 0: {{string 0 1} 2} + 4 le {{list 0 1 {}} 2} + 5 l0:e {{list 0 3 {{string 1 2}}} 4} + 6 li5ee {{list 0 4 {{integer 1 3}}} 5} + 7 li5e4:spame {{list 0 10 {{integer 1 3} {string 4 9}}} 11} + 8 de {{dict 0 1 {}} 2} + 9 d0:0:e {{dict 0 5 {{} {{string 1 2} {string 3 4}}}} 6} + 10 d0:i5ee {{dict 0 6 {{} {{string 1 2} {integer 3 5}}}} 7} + 11 d1:a4:spame {{dict 0 10 {a {{string 1 3} {string 4 9}}}} 11} + 12 ld1:a4:spame3:egge {{list 0 17 {{dict 1 11 {a {{string 2 4} {string 5 10}}}} {string 12 16}}} 18} + 13 13:eggs+spam+ham {{string 0 15} 16} + 14 d1:a1:b1:c1:de {{dict 0 13 {a {{string 1 3} {string 4 6}} c {{string 7 9} {string 10 12}}}} 14} +} { + test bee-11.$n {decoder} { + list [bee::decodeIndices $bee end] $end + } $result ; # {} +} + + +test bee-12.0 {decoder, torrent file} { + set end 0 + + # tcltest::viewFile does not do binary :( + set f [open [file join $::tcltest::testsDirectory example.torrent] r] + fconfigure $f -translation binary + set d [read $f] + close $f + + set data [bee::decode $d end] + + # Cut the binary stuff out of the result, to much, display problems + list [lreplace $data 5 5 [lreplace [lindex $data 5] end end {}]] $end + +} {{announce http://bt.etree.org/announce.php {creation date} 1087598771 info {files {{length 627 path ch1999-05-22.md5} {length 434 path ch1999-05-22.txt} {length 4356201 path ch1999-05-22d1t01.shn} {length 53782885 path ch1999-05-22d1t02.shn} {length 50689401 path ch1999-05-22d1t03.shn} {length 70969629 path ch1999-05-22d1t04.shn} {length 31978833 path ch1999-05-22d1t05.shn} {length 57722005 path ch1999-05-22d1t06.shn} {length 45629997 path ch1999-05-22d2t01.shn} {length 74878121 path ch1999-05-22d2t02.shn} {length 102446341 path ch1999-05-22d2t03.shn} {length 71148293 path ch1999-05-22d2t04.shn}} name ch1999-05-22.schoeps.shnf {piece length} 524288 pieces {}}} 22267} + + + +foreach {n bee result} { + 0 i0e4:hams {value 0 value hams eof {}} + 1 lede {value {{}} value {{}} eof {}} + 2 le3:egg {value {{}} value egg eof {}} + 3 3:eggle {value egg value {{}} eof {}} + 4 de3:egg {value {{}} value egg eof {}} + 5 3:eggde {value egg value {{}} eof {}} + 6 li6e6:plierse3:ham {value {{6 pliers}} value ham eof {}} + 7 7:monitorli6e6:plierse {value monitor value {{6 pliers}} eof {}} + 8 d6:pliersi6ee3:ham {value {{pliers 6}} value ham eof {}} + 9 7:monitord6:pliersi6ee {value monitor value {{pliers 6}} eof {}} +} { + test bee-13.$n {decoder, channel, multiple values} { + set path [gen bee13.$n $bee] + + set res "" ; set tick 1 + bee::decodeChannel [open $path r] \ + -command tick + vwait tick + removeFile bee13.$n + set res + } $result ; # {} + + test bee-14.$n {decoder, channel, multiple values} { + set path [gen bee14.$n $bee] + + set res "" ; set tick 1 + bee::decodeChannel [open $path r] \ + -command tick -exact + vwait tick + removeFile bee14.$n + set res + } $result ; # {} + + test bee-15.$n {decoder, channel, multiple values, abort} { + set path [gen bee15.$n $bee] + + set res "" ; set tick 1 + bee::decodeChannel [set f [open $path r]] \ + -command tickoff + vwait tick + close $f + removeFile bee15.$n + set res + } [lrange $result 0 1] ; # {} + + test bee-16.$n {decoder, channel, multiple values, abort} { + set path [gen bee16.$n $bee] + + set res "" ; set tick 1 + bee::decodeChannel [set f [open $path r]] \ + -command tickoff -exact + vwait tick + close $f + removeFile bee16.$n + set res + } [lrange $result 0 1] ; # {} +} + +# ....... ............................................................ +testsuiteCleanup diff --git a/tcllib/modules/bee/example.torrent b/tcllib/modules/bee/example.torrent Binary files differnew file mode 100644 index 0000000..3421f57 --- /dev/null +++ b/tcllib/modules/bee/example.torrent diff --git a/tcllib/modules/bee/pkgIndex.tcl b/tcllib/modules/bee/pkgIndex.tcl new file mode 100644 index 0000000..e95dedf --- /dev/null +++ b/tcllib/modules/bee/pkgIndex.tcl @@ -0,0 +1,4 @@ +# Tcl package index file, version 1.1 + +if {![package vsatisfies [package provide Tcl] 8.4]} {return} +package ifneeded bee 0.1 [list source [file join $dir bee.tcl]] |