NAME

Linux::Event::Framer::DecimalLength - Frame messages with an ASCII decimal payload length

SYNOPSIS

package SyslogStream;

use parent 'Linux::Event::IO::Sock::Stream';
use Linux::Event::Framer 'DecimalLength',
    separator => ' ',
    max_frame => 1_048_576;

sub on_message ($self, $message) {
    ...
}

DESCRIPTION

Linux::Event::Framer::DecimalLength prefixes each payload with its byte length written as ASCII decimal digits, followed by one separator byte.

The default separator is a space.

For example, the payload:

HELLO

is sent as:

5 HELLO

The 5 says that five payload bytes follow.

This is the framing style used by RFC 6587 octet-counted syslog when the default space separator is used.

WIRE FORMAT

The wire format is:

decimal length
separator
payload

For example:

3 abc

contains:

length       3
separator    space
payload      abc

There is no trailing delimiter.

The declared length tells Linux::Event exactly where the payload ends.

DECLARING THE FRAMER

A typical declaration is:

package SyslogStream;

use parent 'Linux::Event::IO::Sock::Stream';
use Linux::Event::Framer 'DecimalLength';

This uses the default separator:

' '

A different separator can be selected:

use Linux::Event::Framer 'DecimalLength',
    separator => '|';

Then:

$self->send("abc");

produces:

3|abc

THE SEPARATOR

separator

The separator must be exactly one byte.

The default is:

separator => ' '

The separator cannot be an ASCII digit because the parser must be able to tell where the decimal length ends.

For example, this is valid:

separator => '|'

but this is not:

separator => '7'

and neither is this:

separator => '::'

The separator is protocol syntax.

It is not part of the payload length.

RECEIVING

Linux::Event reads decimal digits until it reaches the configured separator.

It decodes that number as the payload length, then waits for exactly that many payload bytes.

For example, with the default separator:

5 HELLO

delivers:

HELLO

to on_message.

The length digits and separator are removed from ordinary inbound delivery.

READ BOUNDARIES DO NOT MATTER

The decimal prefix or payload may be split across kernel reads.

For example:

first read:   12
second read:  8 <some payload bytes>
later reads: <remaining payload bytes>

still forms one message whose payload length is 128 bytes.

Linux::Event retains partial framing state until the complete payload is available.

Application code does not need to reconstruct the prefix or payload itself.

MULTIPLE MESSAGES MAY ARRIVE AT ONCE

One kernel read may contain several complete DecimalLength frames.

For example:

3 one3 two5 three

with a space separator represents three messages:

one
two
three

The parser uses each declared byte length to find the next message boundary.

Kernel read boundaries do not become protocol boundaries.

ZERO-LENGTH MESSAGES

The canonical representation of an empty payload with the default separator is:

0 

That is:

digit zero
followed by one space
followed by no payload bytes

Therefore:

$self->send('');

produces exactly:

0 

With:

separator => '|'

the empty frame is:

0|

CANONICAL DECIMAL LENGTHS

Linux::Event requires the length field to use canonical ASCII decimal form.

For example:

5 HELLO

is valid.

A longer decimal value may not begin with zero.

For example:

05 HELLO

is rejected.

The canonical spelling is:

5 HELLO

Zero itself is valid and is written simply as:

0

This gives each payload length one normal textual representation.

AT LEAST ONE DIGIT IS REQUIRED

The separator cannot appear before any decimal length digits.

For example, with a space separator:

hello

is not a valid DecimalLength frame.

A valid frame must begin with at least one ASCII decimal digit.

LENGTH PARSING IS BOUNDED

Linux::Event does not scan an unlimited number of decimal digits looking for a separator.

The native parser bounds the length field and checks for numeric overflow.

An excessively long, overflowing, or otherwise malformed decimal length is reported as a framing error.

INCLUDING THE PREFIX

include_prefix

By default, the callback receives only the payload.

For example:

use Linux::Event::Framer 'DecimalLength',
    separator => '|';

wire input:

5|hello

delivers:

hello

To include the decimal length and separator:

use Linux::Event::Framer 'DecimalLength',
    separator      => '|',
    include_prefix => 1;

the callback instead receives:

5|hello

include_prefix therefore includes both:

decimal length digits
separator byte

along with the payload.

It affects inbound delivery only.

LIMITING PAYLOAD SIZE

max_frame

An optional max_frame limits the decoded payload length:

use Linux::Event::Framer 'DecimalLength',
    max_frame => 1_048_576;

As soon as the decimal length and separator have been parsed, Linux::Event checks the declared payload length.

If it exceeds max_frame, a framing error is reported without waiting for the oversized payload to arrive.

send also rejects payloads larger than max_frame.

max_frame must be a non-negative integer.

MAX_FRAME AND MAX_BUFFER

max_frame limits payload bytes.

The ordered-byte resource's max_buffer separately limits native input storage.

A DecimalLength frame occupies:

decimal length digits
separator byte
payload bytes

so the complete framed representation must also fit within the resource's input buffer policy.

SENDING

send creates the decimal prefix automatically.

For example:

$self->send("HELLO");

with the default separator writes:

5 HELLO

With:

separator => '|'

the same payload becomes:

5|HELLO

The application supplies only the payload.

Linux::Event calculates its byte length and emits the canonical decimal form.

RAW WRITE

write bypasses framing:

$self->write($bytes);

For example:

$self->write("5 HELLO");

writes those exact bytes.

Ordinary DecimalLength protocol output should normally use send so the declared length cannot accidentally disagree with the payload.

INCLUDE_PREFIX DOES NOT CHANGE SENDING

include_prefix affects inbound delivery only.

For example:

use Linux::Event::Framer 'DecimalLength',
    include_prefix => 1;

still means:

$self->send("HELLO");

writes:

5 HELLO

The application should not prepend the decimal length itself before calling send.

BYTE LENGTH, NOT CHARACTER COUNT

The decimal number represents payload bytes.

DecimalLength framing does not define a character encoding.

If an application sends text, it must first encode that text into the bytes required by the protocol.

Linux::Event then measures those bytes when constructing the length prefix.

PAYLOAD CONTENT

The payload may contain arbitrary bytes, including the configured separator.

For example, with a space separator, the payload itself may contain spaces.

The parser does not search the payload for another separator.

Once the length is known, exactly that many bytes belong to the payload.

NO TRAILING TERMINATOR

Unlike Netstring framing, DecimalLength does not require a trailing comma or other terminator.

For example:

5 HELLO

is already a complete frame.

The payload length alone tells Linux::Event where the next message begins.

This distinction is important:

Netstring
    5:HELLO,

DecimalLength
    5 HELLO

Both use textual lengths, but they are different wire protocols.

MALFORMED INPUT

Linux::Event rejects malformed DecimalLength input.

Examples include:

  • a non-digit in the length field

  • no length digits before the separator

  • a noncanonical leading zero

  • an excessively long decimal length

  • a numeric overflow

  • a declared payload larger than max_frame

  • a complete frame that violates the ordered-byte buffer limit

These use the normal ordered-byte framing error path.

ERROR BEHAVIOR

Malformed or oversized inbound input produces a Linux::Event::Error with type:

framing

The resource then follows its ordinary framing-error lifecycle.

Outbound send rejects a payload larger than max_frame rather than emitting an invalid frame.

WHEN TO USE DECIMALLENGTH

Use DecimalLength when the protocol specifies:

ASCII decimal payload length
one separator byte
payload

It is particularly useful for RFC 6587 octet-counted syslog:

5 HELLO

If the protocol instead uses:

length:payload,

with a trailing comma, use Linux::Event::Framer::Netstring.

If it uses a binary integer prefix, use Linux::Event::Framer::LengthPrefix or Linux::Event::Framer::U32BE.

PERFORMANCE MODEL

Inbound DecimalLength parsing runs in Linux::Event's native ordered-byte parser.

Decimal parsing, canonical-form checks, overflow detection, max_frame enforcement, and message-boundary detection happen before semantic delivery crosses into Perl.

Partial prefixes and payloads remain in native storage until a complete message is available.

Outbound send calculates the payload byte length, converts it to canonical ASCII decimal form, appends the configured separator, and sends the resulting frame through the normal native write path.

SEE ALSO

Linux::Event::Framer, Linux::Event::Framer::Netstring, Linux::Event::Framer::LengthPrefix, Linux::Event::Framer::U32BE, Linux::Event::Framer::Varint, Linux::Event::IO::Sock::Stream, Linux::Event::IO::Pipe, Linux::Event::IO::TTY, docs/FRAMING.md, docs/CHOOSING-A-FRAMER.md.