<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE TIP SYSTEM "http://www.tcl.tk/cgi-bin/tct/tip/tipxml.dtd">
<!-- Converted at Thu May 17 03:35:58 GMT 2012 -->
<!-- TIP AutoGenerator - written by Donal K. Fellows -->

<TIP number='230'>
<header><title>Tcl Channel Transformation Reflection API</title><author address="mailto:andreas_kupries@users.sf.net">Andreas Kupries</author><author address="mailto:akupries@shaw.ca">Andreas Kupries</author><author address="mailto:andreask@activestate.com">Andreas Kupries</author><status type='project' state='final' tclversion="8.6" vote='after'>$Revision: 1.17 $</status><history></history><created day='2' month='nov' year='2004' /></header>
<abstract>This document describes an API which reflects the Channel Transformation API of the core I/O system up into the Tcl level, for the implementation of channel transformations in Tcl. It is built on top of <tipref type="text" tip="208"/> (&apos;Add a chan command&apos;) and also an independent companion to <tipref type="text" tip="219"/> (&apos;Tcl Channel Reflection API&apos;) and <tipref type="text" tip="228"/> (&apos;Tcl Filesystem Reflection API&apos;). As the latter TIPs bring the ability of writing channel drivers and filesystems in Tcl itself so this TIP provides the facilities for the implementation of new channel transformations in Tcl. This document specifies version <emph style="italic">1</emph> of the transformation reflection API.</abstract>
<body><section title="Background and Motivation">
<para>The purpose of this and the other reflection TIPs is to provide all the facilities required for the creation and usage of wrapped files (i.e. virtual filesystems attached to executables and binary libraries) within the core.</para>
<para>While it is possible to implement and place all the proposed reflectivity in separate and external packages, this however means that the core itself cannot make use of wrapping technology and virtual filesystems to encapsulate and attach its own data and library files to itself. Something which is desirable as it can make the deployment and embedding of the core easier, due to having less files to deal with, and a higher degree of self-containment.</para>
<para>One possible application of a completely self-contained core library would be, for example, the Tcl browser plugin.</para>
<para>It is also possible to create a special purpose filesystem and channel driver in the core for this type of thing, but it is my belief that the general purpose framework specified here is a better solution as it will also give users of the core the freedom to experiment with their own ideas, instead of constraining them to what we managed to envision.</para>
<para>Another use for reflected transformations is as a helper for testing the generic I/O layer of Tcl, by creating transformations which forcibly return errors, bogus data, and the like.</para>
<para>An implementation of this TIP is already present in the core, as part of the special test commands for exercising the various internal of the Tcl core during test. This TIP asks to make that mechanism publicly available to script and package authors, with a bit of cleanup regarding the Tcl level API. The roots of that mechanism can be traced back to the Trf package which implemented channel transformations first and provides a similar command.</para>
</section>
<section title="Transform Management API Specification">
<para>The Tcl level API consists of two new subcommands added to the ensemble command <emph style="bold">chan</emph>&apos; specified by <tipref type="text" tip="208"/>. Both subcommands are completely generic, i.e. they can be applied to any type of channel, without restrictions. There is no C API to specify. The Tcl core already has a standard API for the creation of channel transformations from the C level.</para>
<subsection title="The push Subcommand">
<quote><emph style="bold">chan push</emph> <emph style="italic">channel cmdprefix</emph></quote>
<para>This subcommand creates a new script level transformation using the command prefix <emph style="italic">cmdprefix</emph> as its handler and attaches it to the specified channel. The new transformation is always added on top of any other transformations which may be present.</para>
<para>The handle of the new transformation is returned as the result of the command. This handle is the first argument given to all handler methods, to allow the code to distinguish between the various instances of the same transformation, if necessary.</para>
<para>Use the new <emph style="bold">chan pop</emph> command to remove the transformation. See below.</para>
<para>The API this handler has to provide is specified below, in the section &quot;Command Handler API Specification&quot;.</para>
<para>We have chosen to use <emph style="italic">late-binding</emph> of the handler command. See the section &quot;Early- versus Late-Binding of the Handler Command&quot; for more detailed explanations.</para>
<para>The command invokes the handler method <emph style="bold">initialize</emph> to determine the supported methods before it pushes the transformation. It will throw an error if a read-only transformation is pushed on a write-only channel, or vice versa. In general if the r/w-mode of transformation and channel together cause the result to be neither readable nor writable.</para>
</subsection>
<subsection title="The pop Subcommand">
<quote><emph style="bold">chan pop</emph> <emph style="italic">channel</emph></quote>
<para>This subcommand removes the topmost transformation from the <emph style="italic">channel</emph>, if there is any. This command is equivalent to the builtin command <emph style="italic">close</emph> if the channel had no transformations added to it.</para>
<para>Note: If the removal of the topmost transformation uncovers inactive transformations (See section &quot;Interaction with Threads and Other Interpreters&quot;), then these will be removed now as well.</para>
</subsection>
</section>
<section title="Command Handler API Specification">
<para>The Tcl-level handler command for a reflected channel transformation is an ensemble that has to support the following subcommands, as listed below. Note that the term <emph style="italic">ensemble</emph> is used to generically describe all command (prefixes) which are able to process subcommands. This TIP is <emph style="bold">not</emph> tied to the recently introduced &apos;namespace ensemble&apos;s (though they may be used to implement such handlers.)</para>
<subsection title="The initialize Subcommand">
<quote><emph style="italic">handler</emph> <emph style="bold">initialize</emph> <emph style="italic">handle mode</emph></quote>
<para>This method is called first, and then never again (for the given <emph style="italic">handle</emph>). Its responsibility is to initialize all parts of the transformation at the Tcl level. The MODE is a list containing any of <emph style="bold">read</emph> and <emph style="bold">write</emph>.</para>
<itemize><item.i><para><emph style="bold">write</emph> contained in <emph style="italic">mode</emph> implies that the channel is writable.</para></item.i><item.i><para><emph style="bold">read</emph> contained in <emph style="italic">mode</emph> implies that the channel is readable.</para></item.i></itemize>
<para>The return value of the method has to be a list containing the names of all methods which are supported by this handler. Any error thrown by the method will prevent the creation of the transformation. The thrown error will appear as error thrown by <emph style="bold">chan push</emph>.</para>
<para>The current version is <emph style="italic">1</emph>.</para>
<para>This method has no equivalent at the C level.</para>
</subsection>
<subsection title="The finalize Subcommand">
<quote><emph style="italic">handler</emph> <emph style="bold">finalize</emph> <emph style="italic">handle</emph></quote>
<para>This method is called last for the given <emph style="italic">handle</emph>, just before the destruction of the C level data structures. It is now its responsibility to clean up all parts of the transformation at the Tcl level.</para>
<para>Any result returned by the method will be ignored. The same is true for errors thrown by the method.</para>
<para>This method has no equivalent at the C level.</para>
</subsection>
<subsection title="The flush Subcommand">
<quote><emph style="italic">handler</emph> <emph style="bold">flush</emph> <emph style="italic">handle</emph></quote>
<para>This method is called whenever data in the transformation &apos;write&apos; buffer has to be forced downward, i.e. towards the base channel. The result returned by the method is taken as the <emph style="italic">binary</emph> data to write to the transformation below the current transformation. This can be the base channel as well.</para>
<para>In other words, when this method is called the transformation cannot defer the actual transformation operation anymore and has to transform all data waiting in its internal write buffers and return the result of that action.</para>
<para>The method is optional. However if this method is supported then <emph style="bold">write</emph> has to be supported as well. The reverse is not true.</para>
</subsection>
<subsection title="The write Subcommand">
<quote><emph style="italic">handler</emph> <emph style="bold">write</emph> <emph style="italic">handle buffer</emph></quote>
<para>This method is optional. If it is not present it means that the transformation does not support writing, and the channel it was pushed on becomes non-writable as well.</para>
<para>It will be called whenever the user, or a transformation above this transformation writes data downward. The <emph style="italic">buffer</emph> contains the <emph style="bold">binary</emph> data which has been written to us. It is the responsibility of this method to actually transform the data.</para>
<para>The result returned by the method is taken as the <emph style="bold">binary</emph> data to write to the transformation below this transformation. This can be the base channel as well. Note that the result is allowed to be empty, or less than the data we got. The transformation is not required to transform everything which was written to it right now. It is allowed to store this data in internal buffers and to defer the actual transformation until it has more data.</para>
</subsection>
<subsection title="The drain Subcommand">
<quote><emph style="italic">handler</emph> <emph style="bold">drain</emph> <emph style="italic">handle</emph></quote>
<para>This method is called whenever data in the transformation input (i.e. read) buffer has to be forced upward, i.e. towards the user, i.e. the script. The result returned by the method is taken as the <emph style="bold">binary</emph> data to push upward to the transformation above this transformation. This can be the script as well.</para>
<para>In other words, when this method is called the transformation cannot defer the actual transformation operation anymore and has to transform all data waiting in its internal read buffers and return the result of that action.</para>
<para>The method is optional. However if this method is supported then <emph style="bold">read</emph> has to be supported as well. The reverse is not true.</para>
</subsection>
<subsection title="The read Subcommand">
<quote><emph style="italic">handler</emph> <emph style="bold">read</emph> <emph style="italic">handle buffer</emph></quote>
<para>This method is optional. If it is not present it means that the transformation does not support reading, and the channel it was pushed on becomes non-readable as well.</para>
<para>It is called whenever the base channel, or a transformation below this transformation pushes data upward. The <emph style="italic">buffer</emph> contains the <emph style="bold">binary</emph> data which has been given to us from below. It is the responsibility of this method to actually transform data. The result returned by the method is taken as the <emph style="bold">binary</emph> data to push further upward to the transformation above this transformation. This can be the user, i.e. the script as well.</para>
<para>Note that the result is allowed to be empty, or even less than the data we got. The transformation is not required to transform everything given to it right now. It is allowed to store incoming data in internal buffers and to defer the actual transformation until it has more data.</para>
</subsection>
<subsection title="The limit? Subcommand">
<quote><emph style="italic">handler</emph> <emph style="bold">limit?</emph> <emph style="italic">handle</emph></quote>
<para>This method is optional. If it is not present it means that the transformation is allowed to read ahead as much as it likes.</para>
<para>This method is called during input processing and allows the Tcl level part of the transformation to restrict the number of bytes read from the downward transformation or base channel before its method <emph style="bold">read</emph> is called with the resulting buffer.</para>
<para>The result of the method has to be an integer number not equal to zero. A negative result signals that the transformation allows the I/O system to read an unlimited number of bytes. A positive number on the other hand is interpreted as the maximum number of bytes the I/O system is allowed to read from the downward transformation or base channel.</para>
<para>This method is necessary for transformations where the data is bounded at the end in some way. In that case the transformation has to prevent the system from reading beyond the boundary as otherwise data behind it will be given to the transformation and then lost when the transformation is removed. <emph style="bold">This is a limitation of the current I/O core</emph> as it does not allow a transformation to push unused data back into the I/O core when the transformation is removed from the channel.</para>
<para>Fixing this <emph style="bold">limitation</emph> requires a separate TIP, as either the public API of the I/O core has to be extended, or the public structure of channel drivers.</para>
</subsection>
<subsection title="The clear Subcommand">
<quote><emph style="italic">handler</emph> <emph style="bold">clear</emph> <emph style="italic">handle</emph></quote>
<para>This method is called to clear any data stored in the internal input buffers of the transformation. This happens only when the user seeks the channel the transformation is attached to.</para>
<para>Any result returned by the method is ignored.</para>
<para>The method is optional. I.e. a transformation not having any internal buffers to clear can leave out its implementation.</para>
</subsection>
</section>
<section title="Hardwired Behavior of Reflected Transformations">
<para>Not all functions of the channel driver implementing the reflected transformation are directly reflected into the Tcl level. Their behavior is hardwired in the C level implementing the reflection and specified now.</para>
<describe><item.d name='BlockModeProc'><para>Records the chosen blocking mode in the C-level data structures. This influences the low-level write and read behavior. However the Tcl level is shielded from this, so a handler method is not required.</para></item.d><item.d name='CloseProc'><para>Invokes the methods <emph style="bold">drain</emph> and <emph style="bold">flush</emph> to clean up any buffers managed by the Tcl level of the transformation, and then <emph style="bold">finalize</emph>. <emph style="bold">drain</emph> is not called if a previous call to it has not been invalidated yet.</para></item.d><item.d name='InputProc'><para>Tries to satisfies the incoming read request from the input result buffer first. If that is not enough it invokes the <emph style="bold">limit?</emph> method to establish the current read limit, reads data from the downward transformation or base channel per the limit and feeds the data it got to the Tcl level of the transformation, via an invocation of the method <emph style="bold">read</emph>. The result of that call is added to the input result buffer and used to further satisfy the read request.</para><para>If the channel is blocking the system will iterate until the request is fulfilled completely or EOF has been signaled from below.</para><para>In non-blocking mode however the loop will stop if either the request was fulfilled completely, or if we would block. Note that reaching EOF in this situation causes a flush of the Tcl side input buffers via an invocation of the method <emph style="bold">drain</emph>. Otherwise the data stored in them would be lost when the transformation is removed or the channel closed completely.</para></item.d><item.d name='OutputProc'><para>Simply forwards the written buffer to the method <emph style="bold">write</emph> for processing and then writes any returned result to the transformation or base channel below.</para></item.d><item.d name='SeekProc'><para>Recognizes requests made by <emph style="bold">tell</emph> and passes them down without doing anything else. The result generated by the base channel is then passed back up unchanged.</para><para>Any other request causes it to flush all write buffers on the Tcl side via an invocation of the method <emph style="bold">flush</emph>, and clear all input buffers on the Tcl side via an invocation of the method <emph style="bold">clear</emph> before passing the request down. Note that the calls mentioned above are not made if the channel is not writable, or not readable. Further note that the results of the <emph style="bold">flush</emph> are discarded, not written, as they would otherwise move the current location and throw off our expectations regarding where we are now and will end up after the seek.</para><para>This implements the most simple seek behavior as it was found in the very earliest incarnation of the transform reflection functionality, i.e. passing down any seek request unchanged until the base channel is reached. This also means that the internal state of transformations is not adjusted after a seek and may generate bogus results.</para><para>The reflection (actually any transformation) provided by the package <emph style="italic">Trf</emph> has much more complex seek behavior. This was left out for now to keep the scope of this TIP relatively focused. A follow-up TIP can be written for a deeper discussion of the interaction between seeking and any type of transformation, not only reflected ones.</para><para>See <url ref="http://www.oche.de/~akupries/soft/trf/trf_seek.html"/> for the description of the complex seeking model used by the transformation reflection in Trf.</para></item.d><item.d name='SetOptionProc, GetOptionProc, GetFileHandleProc'><para>The calls are passed down without change, any results are passed back to the caller, again without any changes.</para><para>Transformations have no options which can be configured when they are attached to a channel, hence the pass-through and no handler method at the Tcl level.</para></item.d><item.d name='WatchProc'><para>Remembers the interest mask provided by the caller and uses this to manage the internal timer used to generate fake file events when data is buffered.</para></item.d><item.d name='NotifyProc'><para>Manages the internal timer. Will pass the incoming mask of triggered events upward without change.</para></item.d></describe>
</section>
<section title="Interaction with Threads and Other Interpreters">
<para>Adding a reflected transformation to a channel does not create any restrictions on the sharing of the channel with other interpreters, nor with moving the channel to different interpreters or threads.</para>
<para>Like for reflected channels (See <tipref type="text" tip="219"/> (&apos;Tcl Channel Reflection API&apos;)), the implementation ensures that the handler command is always executed in the original interpreter and thread. The latter is done by posting specialized events to the original thread, essentially forwarding driver invocations to the correct thread.</para>
<para>When a thread or interpreter is deleted all transformations it owns are deleted as far as possible, and any remnants are marked as dead. The latter occurs only if the channel using the deleted transformation is outside of the deleted thread or interpreter. Such channels will throw errors when accessed, until the offending transformation is removed from them via <emph style="bold">chan pop</emph> (Multiple pop&apos;s will be necessary if the deleted transformation sits in the middle of a stack).</para>
</section>
<section title="Interaction with Safe Interpreters">
<para>The new subcommands <emph style="bold">push</emph> and <emph style="bold">pop</emph> of <emph style="bold">chan</emph> are both safe and therefore made accessible to safe interpreters.</para>
<para>While <emph style="bold">push</emph>ing a transformation arranges for the execution of code this code is always executed within the safe interpreter, even if the channel was moved (See previous section).</para>
<para>That the data flowing through the channel is modified by the transformation is no problem either, because to attach the transformation to the channel it has to have been given to the safe interpreter in the beginning, in other words, the interpreter doing this already trusted the safe interpreter in some way, and the fact that we can now add a transformation does not change this.</para>
<para>Equivalent reasoning applies if the channel was created by the safe interpreter and then shared/moved into the trusted interpreter. The transformation has no effect on the trust already given to the safe interpreter through the share/move operation.</para>
<para>The same holds for the subcommand <emph style="bold">pop</emph>. If the safe interpreter can execute it on a channel it has the channel already in its possession, either because it created the channel, or because the channel was shared/moved into it.</para>
</section>
<section title="Event processing">
<para>It is specified that reflected transformations do not support any type of user-visible event handling.</para>
<para>The only event handling done is the invisible passing of current interest from higher to lower layers, and the (again invisible) behaviour needed to drive the flushing of transformation buffers to higher layers. See the descriptions of <emph style="italic">WatchProc</emph> and <emph style="italic">NotifyProc</emph> in the section <emph style="bold">Hardwired Behavior of Reflected Transformations</emph>.</para>
<para>This is different from the previous revisions of this TIP which included language and definitions to support event processing by transformations up to and including revision 1.11, i.e. allowing transformations to register interest in events, and then process them.</para>
<para>The main example use case for this was that it would give a transformation the ability to initialize itself with data coming from the channel (like a key exchange) before switching to the plain transformative behaviour. While working on the design of how to support this on the Tcl script level I ran into lots of complicated edge cases and convolutions.</para>
<para>Based on this I have now come to the belief that this has been a misfeature from the beginning and that the use case this was based on (see above) is an example of comingling concerns which should be separate, i.e an example of what should not be done.</para>
<para>The transformation&apos;s concern is transforming data, nothing else. Any parameters needed for this, like keys, seeds, etc. should come from the outside, at the time it is pushed on the channel stack. Initializations like key exchanges, cipher negotiations, etc. should and are not be a concern of the transformation. They should and can be done <emph style="bold">before</emph> the transformation is configured and pushed.</para>
<para>This last means that the removal of user-visible event handling support is by no means a restriction on the type of things we can do. Even so, should we find other use cases for event handling by transformations we can always write another TIP within which we specify how to extend reflected transformations with that feature.</para>
</section>
<section title="Early- versus Late-Binding of the Handler Command">
<para>We have two principal methods for using the handler command. These are called early- and late-binding.</para>
<para><emph style="bold">Early-binding</emph> means that the command implementation to use is determined at the time of the creation of the channel, i.e. when <emph style="italic">chan push</emph> is executed, before any methods are called. Afterward it cannot change. The result of the command resolution is stored internally and used until the channel is destroyed. Renaming the handler command has no effect. In other words, the system will automatically call the command under the new name. The destruction of the handler command is intercepted and causes the channel to close as well.</para>
<para><emph style="bold">Late-binding</emph> means that the handler command is stored internally essentially as a string, and this string is mapped to the implementation to use for each and every call to a method of the handler. Renaming the command, or destroying it means that the next call of a handler method will fail, causing the higher level channel command to fail as well. Depending on the method the error message may not be able to explain the reason of that failure.</para>
<para>Another problem with the late-binding approach is that the context for the resolution of the command name has to be specified explicitly to avoid problems with relative names. Early-binding resolves once, in the context of the <emph style="italic">chan create</emph>. Late-binding performs resolution anywhere where channel commands like <emph style="italic">puts</emph>, <emph style="italic">gets</emph>, etc. are called, i.e. in a random context. To prevent problems with different commands of the same name in several namespace it becomes necessary to force the usage of a specific fixed context for the resolution.</para>
<para>Note that moving a different command into place after renaming the original handler allows the Tcl level to change the implementation dynamically at runtime. This however is not really an advantage over early-binding as the early-bound command can be written such that it delegates to the actual implementation, and that can then be changed dynamically as well.</para>
<para>However, despite all this late binding is so far the method of choice for the implementation of callbacks, be they in Tcl, or Tk; and has been chosen for the reflection as well.</para>
<para>The context for all handler method invokations is the <emph style="bold">global scope</emph>.</para>
</section>
<section title="Limitations">
<para>The method <emph style="bold">limit?</emph> is required to limit reading from below to prevent reading over transformation specific boundaries. This is a direct consequence of not being able to push unused data back into the I/O core when a transformation is removed.</para>
<para>The reflection implements the very simple seek behavior found in the earliest incarnation of this functionality, i.e. passing down any seek request unchanged until the base channel is reached. This also means that the internal state of transformations is not adjusted after a seek and may generate bogus results. The reflection (actually any transformation) provided by the package <emph style="italic">Trf</emph> has much more complex seek behavior. This was left out for now to keep the scope of this TIP relatively focused. A follow-up TIP can be written for a deeper discussion of the interaction between seeking and any type of transformation, not only reflected ones.</para>
<para>See <url ref="http://www.oche.de/~akupries/soft/trf/trf_seek.html"/> for the description of the complex seeking model used by the transformation reflection in Trf.</para>
</section>
<section title="Miscellanea">
<para>The transform reflection API reserves the driver type &quot;tclrtransform&quot; for itself, and uses it to detect its own transformations. Usage of this driver type by other transformations is not allowed.</para>
</section>
<section title="Examples">

<subsection title="Transformation Implementations">
<para>A simple way of implementing new transformations is to use any of the various object systems for Tcl. Create a class for the transformation. <emph style="bold">chan push</emph> the transformation in the constructor for new objects and store the transformation handle. Make the new object the command handler for the transformation. This automatically translates the sub commands for the command handler into object methods. Implement the various methods required. When the object is deleted deactivate the transformation, and delete the object when the channel announces that the transformation has been <emph style="bold">chan pop</emph>ped. This part is a bit tricky, flags have to be used to break the potential cycle.</para>
<para>Another possibility is to implement the command handler as a regular command, together with a creation command wrapping around <emph style="italic">chan push</emph> and a backend which keeps track of all handles created by it and their state, associated data, etc.</para>
</subsection>
<subsection title="Possible Transformations">
<itemize><item.i><para>Identity</para></item.i><item.i><para>Gathering statistics about the channel. The simplest would be to count bytes, for example.</para></item.i><item.i><para>Divert a copy of all data to a separate channel. In other words, spying on the channel.</para></item.i><item.i><para>Give a channel which normally cannot seek backward the ability to do so through buffering (a limited amount of) the data read from it.</para></item.i><item.i><para>Limit reading from a channel to a specific finite number of characters (This can be done via a suitable implementation of the method &quot;limit?&quot; in the transformation handler).</para></item.i></itemize>
</subsection>
</section>
<section title="Reference Implementation">
<para>A variant implementation of this TIP is already present in the core, as part of the special test commands for exercising the various internal of the Tcl core during test.</para>
<para>The relevant files are</para>
<itemize><item.i><para>&quot;tclIOGT.c&quot; (base implementation) and</para></item.i><item.i><para>&quot;tclTest.c&quot; (command interface).</para></item.i></itemize>
<para>The command interface specified here is different from the current interface.</para>
<verbatim><vline encoding='base64'>IHRlc3QgdHJhbnNmb3JtIGNoYW5uZWwgLWNvbW1hbmQgY29tbWFuZA==</vline></verbatim>
<para>versus</para>
<verbatim><vline encoding='base64'>IGNoYW4gcHVzaCBjaGFubmVsIGNtZHByZWZpeA==</vline><vline encoding='base64'>IGNoYW4gcG9w</vline></verbatim>
<para>The handler methods are different as well. This TIP consolidated a number of methods, gave them better names and removed unnecessary arguments. It also removed some limitations.</para>
<para>The actual reference implementation is provided at SourceForge [<url ref="http://sourceforge.net/support/tracker.php?aid=1163274"/>].</para>
</section>
<section title="Comments">
<para>[ Add comments on the document here ]</para>
</section>
<section title="Copyright">
<para>This document has been placed in the public domain.</para>
</section>
</body></TIP>

