Skip to content
Clint Webb edited this page Apr 3, 2018 · 7 revisions

Overview

RISP is a streamable binary encoding sub-protocol that can be used to build up more advanced protocols.

RISP, short for Reduced Instruction Set Protocol, is very loosly-based on the concept of the RISC CPU architecture.

It is a command based protocol that handles very simple primitives:

  • Parameter-less commands
  • Commands with a single Integer parameter (of varying sizes)
  • Commands with a binary stream (of varying lengths), also known as a String.

The Binary string parameter can encode another RISP stream, or anything really.

Since RISP is a Command-based protocol, it is expected that each command should do something simple and discreet.

Technical Details

RISP is encoded using a 16-bit (2 byte) command ID. The command ID uses the high-4 bits to indicate what kind of parameters follow the command. This effectively means that there are ranges of Command ID's that are used for particular parameter types.

The effective ranges are:

Range Type Integer Bytes following Mask Common Name
0x0000 to 0x0fff Integer 1 0.000.xxxx.xxxx.xxxx Char, Byte-Integer (8-bit)
0x1000 to 0x1fff Integer 2 0.001.xxxx.xxxx.xxxx Short Integer (16-bit)
0x2000 to 0x2fff Integer 4 0.010.xxxx.xxxx.xxxx Integer (32-bit)
0x3000 to 0x3fff Integer 8 0.011.xxxx.xxxx.xxxx Long Long (64-bit)
0x4000 to 0x4fff Integer 16 0.100.xxxx.xxxx.xxxx (128-bit)
0x5000 to 0x5fff Integer 32 0.101.xxxx.xxxx.xxxx (256-bit)
0x6000 to 0x6fff Integer 64 0.110.xxxx.xxxx.xxxx (512-bit)
0x7000 to 0x7fff None 0 0.111.xxxx.xxxx.xxxx
0x8000 to 0x8fff String 1 1.000.xxxx.xxxx.xxxx String, Max length of 255 bytes
0x9000 to 0x9fff String 2 1.001.xxxx.xxxx.xxxx String, Max length of 64k
0xa000 to 0xafff String 4 1.010.xxxx.xxxx.xxxx String, Max Length of 4gb
0xb000 to 0xbfff String 8 1.011.xxxx.xxxx.xxxx String, Max Length of 16777216tb
0xc000 to 0xffff None 0 1.1xx.xxxx.xxxx.xxxx

Strings

Strings work by having an initial parameter which is an integer (of different sizes depending on the ID range). That integer value then indicates how many bytes follow which is the string value. It essentially encodes the length of the string, and then the string itself.

The Length part of the String is always treated as an unsigned Integer.

Integers

Integer parameters are treated as signed Integers, however, RISP itself does not care much, you can implement it however you want.

The only exception to this is the Integer part of the Strings. These are always unsigned (negative string lengths don't make sense).

Best Practices.

Commands should be as simple and discreet as possible.

One of the main tenets of RISP is that you make up a complicated protocol by use of small discreet operations. A command should trigger a well defined small task. Many commands would be simply setting some value in the session, and then other commands will trigger actions that will use those values.

A simple example could be:

  USERNAME <string>
  PASSWORD <string>
  LOGIN

In that example, the first command USERNAME simply sets the Username parameter in the session data to that value. (Note that the implementation will need to be sure to keep incoming data separate from running data, just setting a Username variable, shouldn't change the Username that the session is running under. So these operations certainly can have some sanity logic in there.)

The next command can set the PASSWORD parameter.

Then the LOGIN command runs, and it checks the Username and Password params and performs the action. It would likely respond with some command that indicates success or failure. But each individual command does a very discreet task.

Layering the Protocol

In many cases there are advantages in layering the protocol. There is a slight potential performance penalty, but it allows for more detailed protocols.

By Layering, we mean that you might have commands that perform some action, and the parameter for that command is actually a binary stream of a bunch of other RISP commands that set the Data required for that operation.

These protocols are essentially laid out as:

  LOGIN <string>
    USERNAME <string>
    PASSWORD <string>

This also means that each main parameter can have its own protcol insides.

In this example, the RISP stream initially just contains one command, the LOGIN command. That LOGIN command however, has a string parameter. The contents of that string parameter contains more RISP commands, that can be processed again. In this case, that exposes the USERNAME and PASSWORD commands. When all processing of the string parameter is complete, then the LOGIN logic should then attempt to utilise those parameters that were set.

Depending on how complex your operations and protocol is, or your defined security model, each outer command may have completely separate RISP processing of the inner commands. This does generally mean more time developing the code handling the protocol, but ensures that inner commands are not mixed inappropriately.

Hybrid with RISP and JSON

JSON and other text bases protocols are very flexible, but it is difficult to stream it without something encapsulating it. The reason it is hard to stream is because there is no way of knowing the boundaries of any element within it, including the outer element. This means the entire stream needs to be parsed to even know if it is complete. RISP has an advantage in that it knows exactly how many bytes it needs in the buffer to process a command. If you think of RISP as the commands and JSON as an object, it becomes easy to see the advantages of mixing the two.

An example of this is the communications protocol for OpenCluster. It uses RISP to encapsulate the encryption and authentication, and the commands that control the OpenCluster environment. However, most of the commands have a set of parameters, and it uses JSON to include those parameters. This allows for an extremely flexible, simple and streamable protocol that takes advantage of both RISP and JSON. A purely RISP based protocol would be significantly more complex.