Bounds v0.1.0 Bounds View Source

Bounds is a formalization of the {pos, len} tuple that is used in Erlang to slice binaries.

A Bounds value is similar to a Range value, with a few differences:

  • A Bounds value can be zero-length. (A Range value must represent at least one element.)

  • A Bounds value is always ascending. (A Range value may be descending.)

  • A Bounds value always has non-negative values. (A Range value may represent negative values.)

Bounds values are normalized as an interval [lower, upper] for convenience of calculation. All functions in the Bounds module expect a %Bounds{} to have been created by a call to a constructor function (e.g. new/2, from_poslen/1, from_range/1.) If you construct a Bounds value yourself, the following guard must hold:

%Bounds{lower: lower, upper: upper} when
  is_integer(lower) and is_integer(upper) and lower >= 0 and upper >= lower

Enumeration

Like Range, Bounds implements Enumerable; and thus, like Range values, Bounds values can be understood as a compact representation of an equivalent list value.

Unlike Range, Bounds has several decorators which implement Enumerable differently, to represent the different, equivalent abstractions that a Bounds value can be understood as.

A Bounds value, passed directly to Enum functions, will enumerate as a collection of all integer points in the closed interval [lower, upper]. If your algorithm wants "point" Bounds values (values where lower == upper) to be included as values in the enumeration, then this is the approach you want.

A common use-case is enumerating all half-closed intervals of some length n (e.g. [lower, lower + n)) which are contained by a given Bounds value. For example, if the Bounds value represents the bounds of a binary, then you might want the bounds of each byte of the binary: [0, 1), [1, 2), etc. The functions stepwise/2, split_stepwise/2, and partitioned/3 will help with this.

Link to this section Summary

Link to this section Functions

Link to this function

clamp(value_bounds, clamp_bounds) View Source

Link to this function

concat(bounds_a, bounds_b) View Source

Link to this function

difference(bounds, sub_bounds) View Source

Link to this function

partitioned(bounds, step, offset) View Source

Link to this function

split_stepwise(bounds, step) View Source

Splits a Bounds value into three parts, returned as a map with the following keys:

  • :whole: the bounds of the contiguous set of values whose bounds are both 1. within the original bounds, and 2. divisible by step_size. This is equivalent to the union/2 of the stepwise/2 enumeration of the given bounds at the given step_size.
  • :partial_before: the interval extending from the beginning of the original bounds, to the beginning of whole.
  • :partial_after: the interval extending from the end of whole, to the end of the original bounds.

Any/all of the values in this map may turn out to be zero-sized "point" Bounds.

Link to this function

stepwise(bounds, step_size \\ 1) View Source

Returns a Bounds.Stepwise decorator value, which implements Enumerable.

The values enumerated from a Bounds.Stepwise decorator are themselves Bounds values, representing a set of contiguous intervals, each of size step_size. The enumeration always begins with the interval [0, step_size) (if it exists.)

Any bounded interval smaller than step_size is not considered a part of the enumeration.

This enumeration strategy is useful when you have a sequence of unit-sized chunks (like the bytes in a binary), in which a representation for one element is encoded as step_size contiguous chunks. The enumerated values will then represent the bounds of the representations of all potentially-valid elements.

Examples

Get the bounds of each single byte of a binary:

iex> Bounds.from_binary("foo") |> Bounds.stepwise(1) |> Enum.to_list()
[%Bounds{lower: 0, upper: 1}, %Bounds{lower: 1, upper: 2}, %Bounds{lower: 2, upper: 3}]

Get the bounds of a sequence of 32-bit values in a binary:

iex> Bounds.from_binary("abcdEFGH") |> Bounds.stepwise(4) |> Enum.to_list()
[%Bounds{lower: 0, upper: 4}, %Bounds{lower: 4, upper: 8}]
Link to this function

translate(bounds, offset) View Source