6.1.1540 FILL CORE

( c-addr u char -- )

If u is greater than zero, store char in each of u consecutive characters of memory beginning at c-addr.

Testing:

T{ FBUF 0 20 FILL -> }T
T{ SEEBUF -> 00 00 00 }T

T{ FBUF 1 20 FILL -> }T
T{ SEEBUF -> 20 00 00 }T

T{ FBUF 3 20 FILL -> }T
T{ SEEBUF -> 20 20 20 }T

ContributeContributions

NieDzejkobavatar of NieDzejkob [37] Suggested reference implementationSuggested reference implementation2018-01-06 18:08:55

: FILL -ROT 0 ?DO 2DUP C! 1+ LOOP 2DROP ;

alextangentavatar of alextangent

-ROT is not in the standard. A reference implementation should (must?) use standard words.

NieDzejkobavatar of NieDzejkobNew Version: Suggested reference implementation

Hide differences

: FILL -ROT 0 ?DO 2DUP C! 1+ LOOP 2DROP ;

: FILL ( c-char u char -- ) ROT ROT 0 ?DO 2DUP C! CHAR+ LOOP 2DROP ;

The ROT ROT does not seem right to me, but finding anything better has proven not to be simple.

alextangentavatar of alextangent

Consider not using ?DO

: FILL ( c-addr u char -- )
    >R BEGIN DUP 0<> WHILE
      OVER R@ SWAP C!
      1 /STRING
    REPEAT R> DROP ;

ikysilavatar of ikysil

Implementation with BEGIN leaves c-addr u on the stack.

Corrected version:

: FILL ( c-addr u char -- )
   ( If u is greater than zero, store char in each of u consecutive characters of memory beginning at c-addr.)
   >R BEGIN
      DUP 0<>
   WHILE
      OVER R@ SWAP C!
      1 /STRING
   REPEAT
   2DROP
   R> DROP
;

superseeker13avatar of superseeker13

I prefer NieDzejkob's solution. Using a core-ext word, 0<>, in a core word seems to defeat the point of core-ext's optional nature.

EricBlakeavatar of EricBlake

Of course, doing this with a manual loop one char at a time can be slow. If you have an implementation where CHARS does not modify its input (that is, a char occupies one addressable unit), this is likely to be faster:

: FILL ( c-addr u char -- )
  OVER 0= IF DROP 2DROP EXIT THEN \ special case for 0
  #2 PICK C! \ populate first char
  OVER SWAP 1 /STRING ( c-addr c-addr+1 u-1 )
  CMOVE
;

ruvavatar of ruv

where CHARS does not modify its input (that is, a char occupies one addressable unit)

This solution using CMOVE should work regardless of the char size, because CMOVE copies characters (not address units like MOVE), and 1 /STRING ( c-addr1 u1 -- c-addr2 u2 ) produces c-addr2 that is c-addr1 + char-size (if correctly implemented).

AntonErtlavatar of AntonErtl

Using CMOVE (as it is implemented in different ways in many Forth systems) for FILL the way you do is actually very slow on modern CPUs. On a Ryzen 5800X it consumes 7 cycles per byte. See news:2021Sep1.233440@mips.complang.tuwien.ac.at, which also explains how to implement CMOVE more efficiently for this kind of usage (eventually I made a EuroForth 2021 presentation about this topic). But it is more efficient to implement FILL without calling CMOVE, and for the reference implementation, I think that the byte-storing variant is preferable; on fast Forth systems it is even faster than the CMOVE-based variant, by a lot.

Reply New Version