Class RunArray

Inherits From:
    Object
      Collection
        SequenceableCollection
          ArrayedCollection

from the class comment:

My instances provide space-efficient storage of data which tends to be constant over long runs of the possible indices. Essentially repeated values are stored singly and then associated with a "run" length that denotes the number of consecutive occurrences of the value. My two important variables are runs An array of how many elements are in each run values An array of what the value is over those elements The variables lastIndex, lastRun and lastOffset cache the last access so that streaming through RunArrays is not an N-squared process.

The values of the cache variables are set each time an element is accessed. The value ranges of these variables are:

   lastIndex between: 1 and: self size
   lastRun between: 1 and: runs size
   lastOffset between: 0 and: (runs at: lastRun) - 1

The following expression holds after execution of acess method at:

   | lengthOfPreviousRuns |
     lengthOfPreviousRuns 
        := (1 to: lastRun - 1)
              inject: 0
              into: [:sum :runIdx | sum + (runs at: runIdx)].

     lastIndex = (lastOffset + lengthOfPreviousRuns + 1)

This is the postcondition of at:

The instance method
   at: idx setRunOffsetAndValue: aBlock
is used to map an index idx into a run. The method parameter aBlock requires three block arguments:
   [:run :offset :value | <instructions>]
where

All enumeration methods of RunArray that use the method do: evaluate the block once for each element in the RunArray, not once for each run! This is an important difference. Look at this example:

  | runArray cnt result |
   runArray := RunArray new.
   runArray addLast: #hello times: 5000.
   cnt := 0.
   result := runArray allSatisfy: 
        [:element | cnt := cnt + 1.
            element = #hello
        ].
  (Array with: result with: cnt) inspect
  

There is a method that can be used to execute a block once per run. Look at this code:

    | runArray cnt result |
   runArray := RunArray new.
   runArray addLast: #hello times: 5000.
   cnt := 0.
   result := true.
     runArray runsAndValuesDo: 
        [:runLength :value | cnt := cnt + 1. 
            result := result & (value = #hello)
        ].
  (Array with: result with: cnt) inspect

It is tempting to redefine some enumeration methods in RunArray. As an example, here is a local redefinition for allSatisfy:

allSatisfy: aBlock

   "Evaluate aBlock with the elements of the receiver.
    If aBlock returns false for any element return false.
    Otherwise return true."

    values do: [:each | (aBlock value: each) ifFalse: [^ false]].
   ^ true 

Note that for blocks with side effects (like the ones in the examples above) this method will exibit a behavior that differs from the behavior of the inherited method. Note also that is is not possible tosimple redefine the method do: in RunArray since this would change the behavior of methods collect:, select: and reject:.

Class Text

Inherits From:

    Object
      Collection
        SequenceableCollection
          ArrayedCollection

from the class comment:

I represent a character string that has been marked with abstract changes in character appearance. Actual display is performed in the presence of a TextStyle which indicates, for each abstract code, an actual font to be used. A Text associates a set of TextAttributes with each character in its character string. These attributes may be font numbers, emphases such as bold or italic, or hyperlink actions. Font numbers are interpreted relative to whatever textStyle appears, along with the text, in a Paragraph. Since most characters have the same attributes as their neighbors, the attributes are stored in a RunArray for efficiency. Each of my instances has string a String runs a RunArray

Instances of class Text are used to store stylished strings.

The following condition is required to be always true:

  string size = runs size

In pettern terminology, Text is a wrapper. It wraps a string and keeps additional information that connot be kept in the string itself. Method Text class>>fromString: does not create a copy of the string precisely because Text is a wrapper.
Two instances of Text should not share the same string or the same run array.