interface iReader extends BaseIface {}
class cReader extends BaseClass implements iReader {
// ++ CONFIG ++ //
protected const MAX_BUFF_LEN = 1024 * 1024; // whatever seems to work best
public function SType() : string { return 'file-read buffer'; }
protected function ModesArray() : array {
return [
FModeEnum::FILE_READER,
FModeEnum::FLAG_ON, // need to read
];
}
// -- CONFIG -- //
// ++ ACCESS ++ //
// (WAS: "bytes currently remaining to read from source" -- but the code looks like "size of entire file")
protected function AvailSrceByteCount() : int { return $this->DataFile()->Size(); }
// NOTE: This is different from $nSpent because it doesn't include what's in the buffer.
private $nInPtr = NULL;
public function RewindAll() : void {
$this->nInPtr = 0;
parent::RewindAll();
}
// OVERRIDE: need to load file bytes if buffer is empty
public function &CurrentBytes() : string {
$nBuff = $this->AvailBuffByteCount(); // what's in the buffer
#$nFileAll = $this->AvailSrceByteCount();
// 2025-12-11 This duplicates a lot of DiscardBytes().
#$this->AmHere("nBuff=[$nBuff] nFileAll=[$nFileAll]");
if ($nBuff === 0) {
$this->RefillBuffer();
/* moved to RefillBuffer()
$nRoom = self::MAX_BUFF_LEN - $nBuff; // room available
$nFileNew = $nFileAll - $this->nInPtr; // what hasn't been read from the file yet
#$this->AmHere("REFILLING... nFileNew=[$nFileNew]");
if ($nFileNew > 0) {
$nToRead = min($nRoom,$nFileNew);
$oRes = $this->DataFile()->InOut()->Read($nToRead);
$osData = $oRes->QData();
if ($osData->HasIt()) {
$sData = $osData->GetIt();
$nLen = strlen($sData);
#$this->AmHere("ADDING [$nLen] BYTES to buffer");
$this->AddBytesForFetch($sData);
#$this->AmHere('LENGTH of data now: ['.strlen($this->sBuff).']');
#} else {
#$this->AmHere("NO DATA");
}
}
*/
}
return $this->sBuff;
}
protected function RefillBuffer() {
$nBuff = $this->AvailBuffByteCount(); // what's in the buffer
$nRoom = self::MAX_BUFF_LEN - $nBuff; // room available
$nFileAll = $this->AvailSrceByteCount();
$nFileNew = $nFileAll - $this->nInPtr; // what hasn't been read from the file yet
#$this->AmHere("REFILLING... nFileNew=[$nFileNew]");
// ++ DEBUG
$ofLog = ProcAdmin::LogFile()->InOut();
$sMsg = "REFILLING: nBuff=[$nBuff] nRoom=[$nRoom] nFileAll=[$nFileAll] nFileNew=[$nFileNew]";
#$this->AmHere($sMsg);
$ofLog->WriteEntry($sMsg);
// -- DEBUG
if ($nFileNew > 0) {
$nToRead = min($nRoom,$nFileNew);
#$this->AmHere("READING $nToRead bytes");
$oRes = $this->DataFile()->InOut()->Read($nToRead);
$osData = $oRes->QData();
if ($osData->HasIt()) {
$sData = $osData->GetIt();
$nLen = strlen($sData);
$this->nInPtr += $nLen; // increment file-read-pointer
#$this->AmHere("ADDING [$nLen] BYTES to buffer");
$this->AddBytesForFetch($sData);
$ofLog->WriteEntry('FETCHED DATA:'.CRLF.$this->sBuff.CRLF."<<<< END DATA");
#$this->AmHere('LENGTH of data now: ['.strlen($this->sBuff).']');
} else {
#$this->AmHere("NO DATA");
}
} else {
#$this->AmHere('Nothing new to read.');
}
}
// 2025-12-14 experimental
public function RemoveLine() : string {
$sBuff = $this->sBuff;
$dxLine = strpos($sBuff,"\r");
$doLoop = TRUE;
$sLine = '';
while ($doLoop) {
if (is_int($dxLine)) {
// EOL found in buffer -- get text up to that point
$sLine .= substr($sBuff,0,$dxLine);
$nLine = strlen($sLine);
$this->DiscardBytes($nLine+strlen("\n"));
$doLoop = TRUE;
#$this->AmHere("GOT LINE, $dxLine bytes");
} else {
// EOL not found in buffer -- refill it from source
$sLine .= $sBuff;
$nLine = strlen($sLine);
$this->DiscardBytes($nLine);
$this->RefillBuffer();
#$this->AmHere("EOL NOT FOUND; fetched another ".strlen($this->sBuff)." bytes");
}
}
#$this->AmHere('GOT LINE of '.strlen($sLine).' bytes');
return $sLine;
}
public function DiscardBytes(int $nBytes) {
$this->ErrorIfShut('read from');
parent::DiscardBytes($nBytes); // basic buffer maintenance
// Is there room in the buffer for more?
$nBuff = $this->AvailBuffByteCount(); // what's in the buffer
$nRoom = self::MAX_BUFF_LEN - $nBuff; // room available
if ($nRoom > 0) {
$nFileAll = $this->AvailSrceByteCount();
$nFileNew = $nFileAll - $this->nInPtr; // what hasn't been read from the file yet
if ($nFileNew > 0) {
// There's more in the file, so refill the buffer:
$nToRead = min($nRoom,$nFileNew);
$oRes = $this->DataFile()->InOut()->Read($nToRead);
$osData = $oRes->QData();
if ($osData->HasIt()) {
$sData = $osData->GetIt();
$nLen = strlen($sData);
$this->AddBytesForFetch($sData);
}
}
}
$sOut = parent::RemoveBytes(); // grab existing buffer-contents
$this->OnStatusChange();
}
// ACTION: remove all bytes from the buffer. Refill buffer if needed, I guess?
// LATER: might want to add a $nMaxBytes parameter, but for now it doesn't seem needed.
public function RemoveBytes() : string {
$this->ErrorIfShut('read from');
// Is there room in the buffer for more?
$nBuff = $this->AvailBuffByteCount(); // what's in the buffer
#$this->AmHere("Bytes remaining: [$nBuff]");
$nRoom = self::MAX_BUFF_LEN - $nBuff; // room available
#$this->AmHere("Bytes remaining: [$nBuff] / Space remaining: [$nRoom]");
if ($nRoom > 0) {
$nFileAll = $this->AvailSrceByteCount();
#echo $this->DataFile()->Ident()->ReflectThis()->Report();
#$this->AmHere("File bytes: [$nFileAll] in ".$this->DataFile()->Ident()->SpecFull());
$nFileNew = $nFileAll - $this->nInPtr; // what hasn't been read from the file yet
if ($nFileNew > 0) {
// There's more in the file, so refill the buffer:
$nToRead = min($nRoom,$nFileNew);
#echo "ADDING $nToRead BYTES (nFileNew=$nFileNew, nRoom=$nRoom) from file".CRLF;
$oRes = $this->DataFile()->InOut()->Read($nToRead);
$osData = $oRes->QData();
if ($osData->HasIt()) {
$sData = $osData->GetIt();
$nLen = strlen($sData);
$this->AddBytesForFetch($sData);
}
}
}
$sOut = parent::RemoveBytes(); // grab existing buffer-contents
#$this->SetNote('bytes to send: '.strlen($sOut));
$this->OnStatusChange();
return $sOut;
}
// -- ACCESS -- //
// ++ UI OUTPUT ++ //
public function DescribeInline() : string {
$nlRem = $this->AvailSrceByteCount();
$sS = ($nlRem === 1) ? '' : 's';
return "$nlRem byte$sS in file";
}
// -- UI OUTPUT -- //
}