Refactor C constants to be class constants of the C class

This commit is contained in:
Timothy Warren 2019-10-24 12:00:14 -04:00
parent 29026373b8
commit 78a91514b3
4 changed files with 239 additions and 168 deletions

9
kilo
View File

@ -3,7 +3,14 @@
namespace Kilo; namespace Kilo;
require_once __DIR__ . '/src/constants.php'; // -----------------------------------------------------------------------------
// ! App Constants
// -----------------------------------------------------------------------------
define('KILO_VERSION', '0.0.1');
define('KILO_TAB_STOP', 4);
define('KILO_QUIT_TIMES', 3);
require_once __DIR__ . '/src/C.php';
require_once __DIR__ . '/src/functions.php'; require_once __DIR__ . '/src/functions.php';
require_once __DIR__ . '/src/Editor.php'; require_once __DIR__ . '/src/Editor.php';

146
src/C.php Normal file
View File

@ -0,0 +1,146 @@
<?php declare(strict_types=1);
namespace Kilo;
/**
* Just a namespace for C language constants
*/
class C {
// ------------------------------------------------------------------------
// ! Misc I/O constants
// ------------------------------------------------------------------------
public const STDIN_FILENO = 0;
public const STDOUT_FILENO = 1;
public const STDERR_FILENO = 2;
public const TCSAFLUSH = 2;
// ------------------------------------------------------------------------
// ! Termios flags and constants
// ------------------------------------------------------------------------
/* Input modes */
public const IGNBRK = (1 << 0); /* Ignore break condition. */
public const BRKINT = (1 << 1); /* Signal interrupt on break. */
public const IGNPAR = (1 << 2); /* Ignore characters with parity errors. */
public const PARMRK = (1 << 3); /* Mark parity and framing errors. */
public const INPCK = (1 << 4); /* Enable input parity check. */
public const ISTRIP = (1 << 5); /* Strip 8th bit off characters. */
public const INLCR = (1 << 6); /* Map NL to CR on input. */
public const IGNCR = (1 << 7); /* Ignore CR. */
public const ICRNL = (1 << 8); /* Map CR to NL on input. */
public const IXON = (1 << 9); /* Enable start/stop output control. */
public const IXOFF = (1 << 10); /* Enable start/stop input control. */
public const IXANY = (1 << 11); /* Any character will restart after stop. */
public const IMAXBEL = (1 << 13); /* Ring bell when input queue is full. */
public const IUCLC = (1 << 14); /* Translate upper case input to lower case. */
/* Output modes */
public const OPOST = (1 << 0); /* Perform output processing. */
public const ONLCR = (1 << 1); /* Map NL to CR-NL on output. */
public const OXTABS = (1 << 2); /* Expand tabs to spaces. */
public const ONOEOT = (1 << 3); /* Discard EOT (^D) on output. */
public const OCRNL = (1 << 4); /* Map CR to NL. */
public const ONOCR = (1 << 5); /* Discard CR's when on column 0. */
public const ONLRET = (1 << 6); /* Move to column 0 on NL. */
public const NLDLY = (3 << 8); /* NL delay. */
public const NL0 = (0 << 8); /* NL type 0. */
public const NL1 = (1 << 8); /* NL type 1. */
public const TABDLY = (3 << 10 | 1 << 2); /* TAB delay. */
public const TAB0 = (0 << 10); /* TAB delay type 0. */
public const TAB1 = (1 << 10); /* TAB delay type 1. */
public const TAB2 = (2 << 10); /* TAB delay type 2. */
public const TAB3 = (1 << 2); /* Expand tabs to spaces. */
public const CRDLY = (3 << 12); /* CR delay. */
public const CR0 = (0 << 12); /* CR delay type 0. */
public const CR1 = (1 << 12); /* CR delay type 1. */
public const CR2 = (2 << 12); /* CR delay type 2. */
public const CR3 = (3 << 12); /* CR delay type 3. */
public const FFDLY = (1 << 14); /* FF delay. */
public const FF0 = (0 << 14); /* FF delay type 0. */
public const FF1 = (1 << 14); /* FF delay type 1. */
public const BSDLY = (1 << 15); /* BS delay. */
public const BS0 = (0 << 15); /* BS delay type 0. */
public const BS1 = (1 << 15); /* BS delay type 1. */
public const VTDLY = (1 << 16); /* VT delay. */
public const VT0 = (0 << 16); /* VT delay type 0. */
public const VT1 = (1 << 16); /* VT delay type 1. */
public const OLCUC = (1 << 17); /* Translate lower case output to upper case */
public const OFILL = (1 << 18); /* Send fill characters for delays. */
public const OFDEL = (1 << 19); /* Fill is DEL. */
/* Control modes */
public const CIGNORE = (1 << 0); /* Ignore these control flags. */
public const CS5 = 0; /* 5 bits per byte. */
public const CS6 = (1 << 8); /* 6 bits per byte. */
public const CS7 = (1 << 9); /* 7 bits per byte. */
public const CS8 = (C::CS6|C::CS7); /* 8 bits per byte. */
public const CSIZE = (C::CS5|C::CS6|C::CS7|C::CS8); /* Number of bits per byte (mask). */
public const CSTOPB = (1 << 10); /* Two stop bits instead of one. */
public const CREAD = (1 << 11); /* Enable receiver. */
public const PARENB = (1 << 12); /* Parity enable. */
public const PARODD = (1 << 13); /* Odd parity instead of even. */
public const HUPCL = (1 << 14); /* Hang up on last close. */
public const CLOCAL = (1 << 15); /* Ignore modem status lines. */
public const CRTSCTS = (1 << 16); /* RTS/CTS flow control. */
public const CRTS_IFLOW = C::CRTSCTS; /* Compatibility. */
public const CCTS_OFLOW = C::CRTSCTS; /* Compatibility. */
public const CDTRCTS = (1 << 17); /* DTR/CTS flow control. */
public const MDMBUF = (1 << 20); /* DTR/DCD flow control. */
public const CHWFLOW = (C::MDMBUF|C::CRTSCTS|C::CDTRCTS); /* All types of flow control. */
/* Local modes */
public const ECHOKE = (1 << 0); /* Visual erase for KILL. */
public const _ECHOE = (1 << 1); /* Visual erase for ERASE. */
public const ECHOE = C::_ECHOE;
public const _ECHOK = (1 << 2); /* Echo NL after KILL. */
public const ECHOK = C::_ECHOK;
public const _ECHO = (1 << 3); /* Enable echo. */
public const ECHO = C::_ECHO;
public const _ECHONL = (1 << 4); /* Echo NL even if ECHO is off. */
public const ECHONL = C::_ECHONL;
public const ECHOPRT = (1 << 5); /* Hardcopy visual erase. */
public const ECHOCTL = (1 << 6); /* Echo control characters as ^X. */
public const _ISIG = (1 << 7); /* Enable signals. */
public const ISIG = C::_ISIG;
public const _ICANON = (1 << 8); /* Do erase and kill processing. */
public const ICANON = C::_ICANON;
public const ALTWERASE = (1 << 9); /* Alternate WERASE algorithm. */
public const _IEXTEN = (1 << 10); /* Enable DISCARD and LNEXT. */
public const IEXTEN = C::_IEXTEN;
public const EXTPROC = (1 << 11); /* External processing. */
public const _TOSTOP = (1 << 22); /* Send SIGTTOU for background output. */
public const TOSTOP = C::_TOSTOP;
public const FLUSHO = (1 << 23); /* Output being flushed (state). */
public const XCASE = (1 << 24); /* Canonical upper/lower case. */
public const NOKERNINFO = (1 << 25); /* Disable VSTATUS. */
public const PENDIN = (1 << 29); /* Retype pending input (state). */
public const _NOFLSH = (1 << 31); /* Disable flush after interrupt. */
public const NOFLSH = C::_NOFLSH;
/* Control characters */
public const VEOF = 0; /* End-of-file character [ICANON]. */
public const VEOL = 1; /* End-of-line character [ICANON]. */
public const VEOL2 = 2; /* Second EOL character [ICANON]. */
public const VERASE = 3; /* Erase character [ICANON]. */
public const VWERASE = 4; /* Word-erase character [ICANON]. */
public const VKILL = 5; /* Kill-line character [ICANON]. */
public const VREPRINT = 6; /* Reprint-line character [ICANON]. */
public const VINTR = 8; /* Interrupt character [ISIG]. */
public const VQUIT = 9; /* Quit character [ISIG]. */
public const VSUSP = 10; /* Suspend character [ISIG]. */
public const VDSUSP = 11; /* Delayed suspend character [ISIG]. */
public const VSTART = 12; /* Start (X-ON) character [IXON, IXOFF]. */
public const VSTOP = 13; /* Stop (X-OFF) character [IXON, IXOFF]. */
public const VLNEXT = 14; /* Literal-next character [IEXTEN]. */
public const VDISCARD = 15; /* Discard character [IEXTEN]. */
public const VMIN = 16; /* Minimum number of bytes read at once [!ICANON]. */
public const VTIME = 17; /* Time-out value (tenths of a second) [!ICANON]. */
public const VSTATUS = 18; /* Status character [ICANON]. */
public const NCCS = 20; /* Value duplicated in <hurd/tioctl.defs>. */
// ------------------------------------------------------------------------
// ! IOCTL constants
// ------------------------------------------------------------------------
public const TIOCGWINSZ = 0x5413;
}

View File

@ -1,148 +0,0 @@
<?php declare(strict_types=1);
namespace Kilo;
// -----------------------------------------------------------------------------
// ! App Constants
// -----------------------------------------------------------------------------
define('KILO_VERSION', '0.0.1');
define('KILO_TAB_STOP', 4);
define('KILO_QUIT_TIMES', 3);
// -----------------------------------------------------------------------------
// ! Misc I/O constants
// -----------------------------------------------------------------------------
define('STDIN_FILENO', 0);
define('STDOUT_FILENO', 1);
define('STDERR_FILENO', 2);
define('TCSAFLUSH', 2);
// -----------------------------------------------------------------------------
// ! Termios flags and constants
// -----------------------------------------------------------------------------
/* Input modes */
define('IGNBRK', (1 << 0)); /* Ignore break condition. */
define('BRKINT', (1 << 1)); /* Signal interrupt on break. */
define('IGNPAR', (1 << 2)); /* Ignore characters with parity errors. */
define('PARMRK', (1 << 3)); /* Mark parity and framing errors. */
define('INPCK', (1 << 4)); /* Enable input parity check. */
define('ISTRIP', (1 << 5)); /* Strip 8th bit off characters. */
define('INLCR', (1 << 6)); /* Map NL to CR on input. */
define('IGNCR', (1 << 7)); /* Ignore CR. */
define('ICRNL', (1 << 8)); /* Map CR to NL on input. */
define('IXON', (1 << 9)); /* Enable start/stop output control. */
define('IXOFF', (1 << 10)); /* Enable start/stop input control. */
define('IXANY', (1 << 11)); /* Any character will restart after stop. */
define('IMAXBEL', (1 << 13)); /* Ring bell when input queue is full. */
define('IUCLC', (1 << 14)); /* Translate upper case input to lower case. */
/* Output modes */
define('OPOST', (1 << 0)); /* Perform output processing. */
define('ONLCR', (1 << 1)); /* Map NL to CR-NL on output. */
define('OXTABS', (1 << 2)); /* Expand tabs to spaces. */
define('ONOEOT', (1 << 3)); /* Discard EOT (^D) on output. */
define('OCRNL', (1 << 4)); /* Map CR to NL. */
define('ONOCR', (1 << 5)); /* Discard CR's when on column 0. */
define('ONLRET', (1 << 6)); /* Move to column 0 on NL. */
define('NLDLY', (3 << 8)); /* NL delay. */
define('NL0', (0 << 8)); /* NL type 0. */
define('NL1', (1 << 8)); /* NL type 1. */
define('TABDLY', (3 << 10 | 1 << 2)); /* TAB delay. */
define('TAB0', (0 << 10)); /* TAB delay type 0. */
define('TAB1', (1 << 10)); /* TAB delay type 1. */
define('TAB2', (2 << 10)); /* TAB delay type 2. */
define('TAB3', (1 << 2)); /* Expand tabs to spaces. */
define('CRDLY', (3 << 12)); /* CR delay. */
define('CR0', (0 << 12)); /* CR delay type 0. */
define('CR1', (1 << 12)); /* CR delay type 1. */
define('CR2', (2 << 12)); /* CR delay type 2. */
define('CR3', (3 << 12)); /* CR delay type 3. */
define('FFDLY', (1 << 14)); /* FF delay. */
define('FF0', (0 << 14)); /* FF delay type 0. */
define('FF1', (1 << 14)); /* FF delay type 1. */
define('BSDLY', (1 << 15)); /* BS delay. */
define('BS0', (0 << 15)); /* BS delay type 0. */
define('BS1', (1 << 15)); /* BS delay type 1. */
define('VTDLY', (1 << 16)); /* VT delay. */
define('VT0', (0 << 16)); /* VT delay type 0. */
define('VT1', (1 << 16)); /* VT delay type 1. */
define('OLCUC', (1 << 17)); /* Translate lower case output to upper case */
define('OFILL', (1 << 18)); /* Send fill characters for delays. */
define('OFDEL', (1 << 19)); /* Fill is DEL. */
/* Control modes */
define('CIGNORE', (1 << 0)); /* Ignore these control flags. */
define('CS5', 0); /* 5 bits per byte. */
define('CS6', (1 << 8)); /* 6 bits per byte. */
define('CS7', (1 << 9)); /* 7 bits per byte. */
define('CS8', (CS6|CS7)); /* 8 bits per byte. */
define('CSIZE', (CS5|CS6|CS7|CS8)); /* Number of bits per byte (mask). */
define('CSTOPB', (1 << 10)); /* Two stop bits instead of one. */
define('CREAD', (1 << 11)); /* Enable receiver. */
define('PARENB', (1 << 12)); /* Parity enable. */
define('PARODD', (1 << 13)); /* Odd parity instead of even. */
define('HUPCL', (1 << 14)); /* Hang up on last close. */
define('CLOCAL', (1 << 15)); /* Ignore modem status lines. */
define('CRTSCTS', (1 << 16)); /* RTS/CTS flow control. */
define('CRTS_IFLOW', CRTSCTS); /* Compatibility. */
define('CCTS_OFLOW', CRTSCTS); /* Compatibility. */
define('CDTRCTS', (1 << 17)); /* DTR/CTS flow control. */
define('MDMBUF', (1 << 20)); /* DTR/DCD flow control. */
define('CHWFLOW', (MDMBUF|CRTSCTS|CDTRCTS)); /* All types of flow control. */
/* Local modes */
define('ECHOKE', (1 << 0)); /* Visual erase for KILL. */
define('_ECHOE', (1 << 1)); /* Visual erase for ERASE. */
define('ECHOE', _ECHOE);
define('_ECHOK', (1 << 2)); /* Echo NL after KILL. */
define('ECHOK', _ECHOK);
define('_ECHO', (1 << 3)); /* Enable echo. */
define('ECHO', _ECHO);
define('_ECHONL', (1 << 4)); /* Echo NL even if ECHO is off. */
define('ECHONL', _ECHONL);
define('ECHOPRT', (1 << 5)); /* Hardcopy visual erase. */
define('ECHOCTL', (1 << 6)); /* Echo control characters as ^X. */
define('_ISIG', (1 << 7)); /* Enable signals. */
define('ISIG', _ISIG);
define('_ICANON', (1 << 8)); /* Do erase and kill processing. */
define('ICANON', _ICANON);
define('ALTWERASE', (1 << 9)); /* Alternate WERASE algorithm. */
define('_IEXTEN', (1 << 10)); /* Enable DISCARD and LNEXT. */
define('IEXTEN', _IEXTEN);
define('EXTPROC', (1 << 11)); /* External processing. */
define('_TOSTOP', (1 << 22)); /* Send SIGTTOU for background output. */
define('TOSTOP', _TOSTOP);
define('FLUSHO', (1 << 23)); /* Output being flushed (state). */
define('XCASE', (1 << 24)); /* Canonical upper/lower case. */
define('NOKERNINFO', (1 << 25)); /* Disable VSTATUS. */
define('PENDIN', (1 << 29)); /* Retype pending input (state). */
define('_NOFLSH', (1 << 31)); /* Disable flush after interrupt. */
define('NOFLSH', _NOFLSH);
/* Control characters */
define('VEOF', 0); /* End-of-file character [ICANON]. */
define('VEOL', 1); /* End-of-line character [ICANON]. */
define('VEOL2', 2); /* Second EOL character [ICANON]. */
define('VERASE', 3); /* Erase character [ICANON]. */
define('VWERASE', 4); /* Word-erase character [ICANON]. */
define('VKILL', 5); /* Kill-line character [ICANON]. */
define('VREPRINT', 6); /* Reprint-line character [ICANON]. */
define('VINTR', 8); /* Interrupt character [ISIG]. */
define('VQUIT', 9); /* Quit character [ISIG]. */
define('VSUSP', 10); /* Suspend character [ISIG]. */
define('VDSUSP', 11); /* Delayed suspend character [ISIG]. */
define('VSTART', 12); /* Start (X-ON) character [IXON, IXOFF]. */
define('VSTOP', 13); /* Stop (X-OFF) character [IXON, IXOFF]. */
define('VLNEXT', 14); /* Literal-next character [IEXTEN]. */
define('VDISCARD', 15); /* Discard character [IEXTEN]. */
define('VMIN', 16); /* Minimum number of bytes read at once [!ICANON]. */
define('VTIME', 17); /* Time-out value (tenths of a second) [!ICANON]. */
define('VSTATUS', 18); /* Status character [ICANON]. */
define('NCCS', 20); /* Value duplicated in <hurd/tioctl.defs>. */
// -----------------------------------------------------------------------------
// ! IOCTL constants
// -----------------------------------------------------------------------------
define('TIOCGWINSZ', 0x5413);

View File

@ -4,8 +4,8 @@ namespace Kilo;
use FFI; use FFI;
$ffi = FFI::load(__DIR__ . '/ffi.h'); /** @var FFI\CData $original_termios */
$original_termios = $ffi->new('struct termios'); $original_termios = get_ffi()->new('struct termios');
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// ! Raw mode / Terminal size // ! Raw mode / Terminal size
@ -13,13 +13,15 @@ $original_termios = $ffi->new('struct termios');
function enable_raw_mode(): void function enable_raw_mode(): void
{ {
global $ffi;
global $original_termios; global $original_termios;
// Make sure to restore normal mode on exit/die/crash
register_shutdown_function('Kilo\disable_raw_mode'); register_shutdown_function('Kilo\disable_raw_mode');
$ffi = get_ffi();
// Populate the original terminal settings // Populate the original terminal settings
$res = $ffi->tcgetattr(STDIN_FILENO, FFI::addr($original_termios)); $res = $ffi->tcgetattr(C::STDIN_FILENO, FFI::addr($original_termios));
if ($res === -1) if ($res === -1)
{ {
die('tcgetattr'); die('tcgetattr');
@ -27,15 +29,15 @@ function enable_raw_mode(): void
// So, the only thing that seems to really matter here is that c_oflag is 0... // So, the only thing that seems to really matter here is that c_oflag is 0...
$termios = clone $original_termios; $termios = clone $original_termios;
$termios->c_iflag = 0; //$termios->c_iflag & ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); $termios->c_iflag = 0; //$termios->c_iflag & ~(C::BRKINT | C::ICRNL | C::INPCK | C::ISTRIP | C::IXON);
$termios->c_oflag = 0; // $termios->c_oflag && ~(OPOST); $termios->c_oflag = 0; // $termios->c_oflag && ~(C::OPOST);
$termios->c_cflag |= (CS8); $termios->c_cflag |= (C::CS8);
$termios->c_lflag = $termios->c_lflag & ~(_ECHO | ICANON | IEXTEN | ISIG); $termios->c_lflag = $termios->c_lflag & ~( C::ECHO | C::ICANON | C::IEXTEN | C::ISIG);
$termios->c_cc[VMIN] = 0; $termios->c_cc[C::VMIN] = 0;
$termios->c_cc[VTIME] = 1; $termios->c_cc[C::VTIME] = 1;
// Turn on raw mode // Turn on raw mode
$res = $ffi->tcsetattr(STDIN_FILENO, TCSAFLUSH, FFI::addr($termios)); $res = $ffi->tcsetattr(C::STDIN_FILENO, C::TCSAFLUSH, FFI::addr($termios));
if ($res === -1) if ($res === -1)
{ {
die('tcsetattr'); die('tcsetattr');
@ -44,10 +46,10 @@ function enable_raw_mode(): void
function disable_raw_mode(): void function disable_raw_mode(): void
{ {
global $ffi;
global $original_termios; global $original_termios;
$res = $ffi->tcsetattr(STDIN_FILENO, TCSAFLUSH, FFI::addr($original_termios)); $ffi = get_ffi();
$res = $ffi->tcsetattr(C::STDIN_FILENO, C::TCSAFLUSH, FFI::addr($original_termios));
echo "\n"; echo "\n";
@ -82,10 +84,10 @@ function get_cursor_position()
function get_window_size() function get_window_size()
{ {
global $ffi; $ffi = get_ffi();
$ws = $ffi->new('struct winsize'); $ws = $ffi->new('struct winsize');
$res = $ffi->ioctl(STDOUT_FILENO, TIOCGWINSZ, FFI::addr($ws)); $res = $ffi->ioctl(C::STDOUT_FILENO, C::TIOCGWINSZ, FFI::addr($ws));
if ($res === -1 || $ws->ws_col === 0) if ($res === -1 || $ws->ws_col === 0)
{ {
@ -95,8 +97,6 @@ function get_window_size()
return [$ws->ws_row, $ws->ws_col]; return [$ws->ws_row, $ws->ws_col];
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// ! C function/macro equivalents // ! C function/macro equivalents
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -115,11 +115,18 @@ function ctrl_key(string $char): int
return -1; return -1;
} }
// b1,100,001 (a) & b0,011,111 = b0,000,001 // b1,100,001 (a) & b0,011,111 (0x1f) = b0,000,001 (SOH)
// b1,100,010 (b) & b0,011,111 = b0,000,010 // b1,100,010 (b) & b0,011,111 (0x1f) = b0,000,010 (STX)
// ...and so on
return ord($char) & 0x1f; return ord($char) & 0x1f;
} }
/**
* Does the one-character string contain an ascii ordinal value?
*
* @param string $single_char
* @return bool
*/
function is_ascii(string $single_char): bool function is_ascii(string $single_char): bool
{ {
if (strlen($single_char) > 1) if (strlen($single_char) > 1)
@ -130,12 +137,24 @@ function is_ascii(string $single_char): bool
return ord($single_char) < 0x80; return ord($single_char) < 0x80;
} }
/**
* Does the one-character string contain an ascii control character?
*
* @param string $char
* @return bool
*/
function is_cntrl(string $char): bool function is_cntrl(string $char): bool
{ {
$c = ord($char); $c = ord($char);
return is_ascii($char) && ( $c < 0x20 || $c === 0x7f ); return is_ascii($char) && ( $c < 0x20 || $c === 0x7f );
} }
/**
* Does the one-character string contain an ascii number?
*
* @param string $char
* @return bool
*/
function is_digit(string $char): bool function is_digit(string $char): bool
{ {
$c = ord($char); $c = ord($char);
@ -146,6 +165,29 @@ function is_digit(string $char): bool
// ! Helper functions // ! Helper functions
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/**
* A 'singleton' function to replace a global variable
*
* @return FFI
*/
function get_ffi(): FFI
{
static $ffi;
if ($ffi === NULL)
{
$ffi = FFI::load(__DIR__ . '/ffi.h');
}
return $ffi;
}
/**
* Pull input from the stdin stream.
*
* @param int $len
* @return string
*/
function read_stdin(int $len = 128): string function read_stdin(int $len = 128): string
{ {
$handle = fopen('php://stdin', 'rb'); $handle = fopen('php://stdin', 'rb');
@ -155,6 +197,13 @@ function read_stdin(int $len = 128): string
return $input; return $input;
} }
/**
* Write to the stdout stream
*
* @param string $str
* @param int|NULL $len
* @return int
*/
function write_stdout(string $str, int $len = NULL): int function write_stdout(string $str, int $len = NULL): int
{ {
$handle = fopen('php://stdout', 'ab'); $handle = fopen('php://stdout', 'ab');
@ -177,3 +226,20 @@ function read_stdout(int $len = 128): string
return $input; return $input;
} }
/**
* Replaces a slice of an array with the same value
*
* @param array $array The array to update
* @param int $offset The index of the first location to update
* @param int $length The number of indices to update
* @param mixed $value The value to replace in the range
*/
function array_replace_range(array &$array, int $offset, int $length, $value):void
{
$end = $offset + $length;
for ($i = $offset; $i < $end; $i++)
{
$array[$i] = $value;
}
}