The wordchan library is an implementation of word channels. A previous version is included in ycl as word.tcl.
The library's word channels transmit words of up to 4095 bytes in length. This may be a fundamental limitation in Tcl builds that read/write 4096 bytes at a time. Working with longer words would require a way to tell apart a split word and two consecutive words. You could accomplish this with in-band signaling (for example, by only sending quoted values). The result would arguably no longer be a word channel, as it would force the user to perform an escape/unescape step instead of simply writing and reading words.
#! /usr/bin/env tclsh
# Copyright (c) 2015, 2023 D. Bohdan
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
namespace eval ::wordchan {
namespace export initialize watch read write
namespace ensemble create
variable maxBytes 4095
variable storage {}
variable version 0.1.1
}
proc ::wordchan::initialize {channelId mode} {
variable storage
dict set storage $channelId data {}
dict set storage $channelId ticktock 0
return [list initialize finalize watch read write]
}
proc ::wordchan::finalize channelId {
variable storage
dict unset storage $channelId
}
proc ::wordchan::watch {channelId eventspec} {}
proc ::wordchan::read {channelId count} {
# We ignore $count.
variable storage
# Force one value to be read at a time.
if {[TickTock $channelId]} {
error EAGAIN
} else {
set chanData [lassign [dict get $storage $channelId data] head]
dict set storage $channelId data $chanData
return $head
}
}
proc ::wordchan::write {channelId data} {
variable maxBytes
variable storage
if {[string length [encoding convertto $data]] > $maxBytes} {
error [list written value [string range $data 0 31]... exceeds \
$maxBytes bytes]
}
set chanData [dict get $storage $channelId data]
dict set storage $channelId data [list {*}$chanData $data]
return [string length $data]
}
# Private API.
proc ::wordchan::TickTock {channelId} {
variable storage
set ticktock [dict get $storage $channelId ticktock]
dict set storage $channelId ticktock [expr {!$ticktock}]
return $ticktock
}
proc ::wordchan::Test {} {
variable maxBytes
set ch [chan create {read write} ::wordchan]
chan configure $ch -buffering none
puts -nonewline $ch [list a]
puts -nonewline $ch [list b c]
set readRes [::read $ch]
if {$readRes ne {a}} {
error [list wrong value: $readRes]
}
set readRes [::read $ch]
if {$readRes ne {b c}} {
error [list wrong value: $readRes]
}
for {set i 0} {$i <= $maxBytes} {incr i} {
set id [format %04u- $i]
set value $id[string repeat x [expr { $i - [string length $id] }]]
puts -nonewline $ch $value
if {[set readRes [::read $ch]] != $value} {
error [list wrote $value but read $readRes]
}
}
catch {
puts -nonewline $ch +[string repeat x $maxBytes]
} err
if {![regexp exceeds $err]} {
error [list did not get the expected error message in $err]
}
}
# If this is the main script...
if {[info exists argv0] && ([file tail [info script]] eq [file tail $argv0])} {
::wordchan::Test
}#! /usr/bin/env tclsh
source wordchan.tcl
proc main {} {
set ch [chan create {read write} ::wordchan]
chan configure $ch -buffering none
puts -nonewline $ch HELLO
puts -nonewline $ch WORLD
puts [read $ch] ;# Prints "HELLO".
puts [read $ch] ;# Prints "WORLD".
}
main