Skip to content

Commit 69c6031

Browse files
committed
Optimized FFileReader
Taking into account that most of read calls will be done for sequential reads of small data.
1 parent 19986c6 commit 69c6031

File tree

3 files changed

+140
-68
lines changed

3 files changed

+140
-68
lines changed

Unreal/UnCore.h

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -762,14 +762,9 @@ class FFileArchive : public FArchive
762762
FFileArchive(const char *Filename, unsigned InOptions);
763763
virtual ~FFileArchive();
764764

765-
virtual void Seek(int Pos);
766-
virtual void Seek64(int64 Pos);
767-
virtual int Tell() const;
768-
virtual int64 Tell64() const;
769765
virtual int GetFileSize() const;
770766
// virtual int64 GetFileSize64() const; -- implemented in derived classes
771767

772-
virtual bool IsEof() const;
773768
virtual bool IsOpen() const;
774769
virtual void Close();
775770

@@ -778,12 +773,10 @@ class FFileArchive : public FArchive
778773
unsigned Options;
779774
const char *FullName; // allocated with appStrdup
780775
const char *ShortName; // points to FullName[N]
781-
int64 FileSize;
782776

783777
byte* Buffer;
784778
int BufferSize;
785779
int64 BufferPos; // position of Buffer in file
786-
int64 ArPos64;
787780
int64 FilePos; // where 'f' position points to (when reading, it usually equals to 'BufferPos + BufferSize')
788781

789782
bool OpenFile();
@@ -811,7 +804,18 @@ class FFileReader : public FFileArchive
811804

812805
virtual void Serialize(void *data, int size);
813806
virtual bool Open();
807+
virtual void Seek(int Pos);
808+
virtual void Seek64(int64 Pos);
809+
virtual int Tell() const;
810+
virtual int64 Tell64() const;
814811
virtual int64 GetFileSize64() const;
812+
virtual bool IsEof() const;
813+
814+
protected:
815+
int64 SeekPos;
816+
int64 FileSize;
817+
int BufferBytesLeft;
818+
int LocalReadPos;
815819
};
816820

817821

@@ -825,11 +829,19 @@ class FFileWriter : public FFileArchive
825829
virtual void Serialize(void *data, int size);
826830
virtual bool Open();
827831
virtual void Close();
832+
virtual void Seek(int Pos);
833+
virtual void Seek64(int64 Pos);
834+
virtual int Tell() const;
835+
virtual int64 Tell64() const;
828836
virtual int64 GetFileSize64() const;
837+
virtual bool IsEof() const;
829838

830839
static void CleanupOnError();
831840

832841
protected:
842+
int64 FileSize;
843+
int64 ArPos64;
844+
833845
void FlushBuffer();
834846
};
835847

@@ -2338,7 +2350,7 @@ void appReadCompressedChunk(FArchive &Ar, byte *Buffer, int Size, int Compressio
23382350
//#define BULKDATA_Unused 0x0020 // the same value as for UE3
23392351
#define BULKDATA_ForceInlinePayload 0x0040 // bulk data stored immediately after header
23402352
#define BULKDATA_PayloadInSeperateFile 0x0100 // data stored in .ubulk file near the asset (UE4.12+)
2341-
#define BULKDATA_SerializeCompressedBitWindow 0x0200 // use platform-specific compression
2353+
#define BULKDATA_SerializeCompressedBitWindow 0x0200 // use platform-specific compression (deprecated, seems not used)
23422354
#define BULKDATA_OptionalPayload 0x0800 // same as BULKDATA_PayloadInSeperateFile, but stored with .uptnl extension (UE4.20+)
23432355
#define BULKDATA_Size64Bit 0x2000 // 64-bit size fields, UE4.22+
23442356

Unreal/UnCoreSerialize.cpp

Lines changed: 120 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -495,11 +495,9 @@ void FArchive::Printf(const char *fmt, ...)
495495
FFileArchive::FFileArchive(const char *Filename, unsigned InOptions)
496496
: Options(InOptions)
497497
, f(NULL)
498-
, FileSize(-1)
499498
, Buffer(NULL)
500499
, BufferPos(0)
501500
, BufferSize(0)
502-
, ArPos64(0)
503501
{
504502
// process the filename
505503
FullName = appStrdup(Filename);
@@ -514,40 +512,13 @@ FFileArchive::~FFileArchive()
514512
appFree(const_cast<char*>(FullName));
515513
}
516514

517-
void FFileArchive::Seek(int Pos)
518-
{
519-
ArPos64 = Pos;
520-
}
521-
522-
void FFileArchive::Seek64(int64 Pos)
523-
{
524-
ArPos64 = Pos;
525-
}
526-
527-
int FFileArchive::Tell() const
528-
{
529-
guard(FFileArchive::Tell());
530-
return (int)ArPos64;
531-
unguard;
532-
}
533-
534-
int64 FFileArchive::Tell64() const
535-
{
536-
return ArPos64;
537-
}
538-
539515
int FFileArchive::GetFileSize() const
540516
{
541517
int64 size = GetFileSize64();
542518
if (size >= (1LL << 31)) appError("GetFileSize returns 0x%llX", size);
543519
return (int)size;
544520
}
545521

546-
bool FFileArchive::IsEof() const
547-
{
548-
return ArPos64 >= GetFileSize64();
549-
}
550-
551522
// this function is useful only for FRO_NoOpenError mode
552523
bool FFileArchive::IsOpen() const
553524
{
@@ -570,7 +541,7 @@ bool FFileArchive::OpenFile()
570541
guard(FFileArchive::OpenFile);
571542
assert(!IsOpen());
572543

573-
ArPos64 = FilePos = 0;
544+
FilePos = 0;
574545
Buffer = (byte*)appMalloc(FILE_BUFFER_SIZE);
575546
BufferPos = 0;
576547
BufferSize = 0;
@@ -597,6 +568,10 @@ bool FFileArchive::OpenFile()
597568

598569
FFileReader::FFileReader(const char *Filename, unsigned InOptions)
599570
: FFileArchive(Filename, InOptions)
571+
, SeekPos(-1)
572+
, FileSize(-1)
573+
, BufferBytesLeft(0)
574+
, LocalReadPos(0)
600575
{
601576
guard(FFileReader::FFileReader);
602577
IsLoading = true;
@@ -615,62 +590,79 @@ void FFileReader::Serialize(void *data, int size)
615590

616591
assert(data);
617592

618-
if (ArStopper > 0 && ArPos64 + size > ArStopper)
619-
appError("Serializing behind stopper (%llX+%X > %X)", ArPos64, size, ArStopper);
593+
if (ArStopper > 0 && LocalReadPos + size > ArStopper - BufferPos)
594+
appError("Serializing behind stopper (%llX+%X > %X)", BufferPos + LocalReadPos, size, ArStopper);
620595

596+
// The function is optimized for calling frequently with reading data from buffer
621597
while (size > 0)
622598
{
623-
int64 LocalPos64 = ArPos64 - BufferPos;
624-
if (LocalPos64 < 0 || LocalPos64 >= BufferSize)
599+
if (BufferBytesLeft > 0)
625600
{
626-
// seek to desired position if needed
627-
if (ArPos64 != FilePos)
601+
// Use the buffer
602+
byte* BufferPtr = Buffer + LocalReadPos;
603+
int CanCopy = size > BufferBytesLeft ? BufferBytesLeft : size;
604+
// Copy data. If we're copying 1-2-4 bytes, "special" code works faster than the case with memcpy.
605+
switch (CanCopy)
628606
{
629-
if (fseeko64(f, ArPos64, SEEK_SET) != 0)
630-
appError("Error seeking to position 0x%llX", ArPos64);
631-
FilePos = ArPos64;
607+
case 1:
608+
*(byte*)data = *(BufferPtr);
609+
break;
610+
case 2:
611+
*(uint16*)data = *(uint16*)BufferPtr;
612+
break;
613+
case 4:
614+
*(uint32*)data = *(uint32*)BufferPtr;
615+
break;
616+
default:
617+
memcpy(data, BufferPtr, CanCopy);
632618
}
633-
// the requested data is not in buffer
634-
if (size >= FILE_BUFFER_SIZE)
619+
// Advance pointers
620+
BufferBytesLeft -= CanCopy;
621+
data = OffsetPointer(data, CanCopy);
622+
size -= CanCopy;
623+
LocalReadPos += CanCopy;
624+
}
625+
else
626+
{
627+
// Buffer is empty
628+
if (SeekPos >= 0)
629+
{
630+
// Seek to desired position
631+
if (SeekPos != FilePos)
632+
{
633+
if (fseeko64(f, SeekPos, SEEK_SET) != 0)
634+
appError("Error seeking to position 0x%llX", SeekPos);
635+
FilePos = SeekPos;
636+
}
637+
SeekPos = -1;
638+
}
639+
if (size >= FILE_BUFFER_SIZE / 2)
635640
{
636-
// large block, read directly from file
641+
// Large block, read directly to destination skipping buffer
637642
int res = fread(data, size, 1, f);
638643
if (res != 1)
639-
appError("Unable to read %d bytes at pos=0x%llX", size, ArPos64);
644+
appError("Unable to read %d bytes at pos=0x%llX", size, FilePos);
640645
#if PROFILE
641646
GNumSerialize++;
642647
GSerializeBytes += size;
643648
#endif
644-
ArPos64 += size;
645649
FilePos += size;
646650
return;
647651
}
648-
// fill buffer
652+
// Fill buffer
649653
int ReadBytes = fread(Buffer, 1, FILE_BUFFER_SIZE, f);
650654
if (ReadBytes == 0)
651-
appError("Unable to read %d bytes at pos=0x%llX", 1, ArPos64);
655+
appError("Unable to read %d bytes at pos=0x%llX", 1, FilePos);
652656
#if PROFILE
653657
GNumSerialize++;
654658
GSerializeBytes += ReadBytes;
655659
#endif
656660
BufferPos = FilePos;
657661
BufferSize = ReadBytes;
658662
FilePos += ReadBytes;
659-
// update LocalPos
660-
LocalPos64 = ArPos64 - BufferPos;
661-
assert(LocalPos64 >= 0 && LocalPos64 < BufferSize);
663+
BufferBytesLeft = ReadBytes;
664+
LocalReadPos = 0;
662665
}
663-
664-
// here we have 32-bit position in buffer
665-
int LocalPos = (int)LocalPos64;
666-
667-
// have something in buffer
668-
int CanCopy = BufferSize - LocalPos;
669-
if (CanCopy > size) CanCopy = size;
670-
memcpy(data, Buffer + LocalPos, CanCopy);
671-
data = OffsetPointer(data, CanCopy);
672-
size -= CanCopy;
673-
ArPos64 += CanCopy;
674666
}
675667

676668
unguardf("File=%s", ShortName);
@@ -681,6 +673,41 @@ bool FFileReader::Open()
681673
return OpenFile();
682674
}
683675

676+
void FFileReader::Seek(int Pos)
677+
{
678+
Seek64(Pos);
679+
}
680+
681+
void FFileReader::Seek64(int64 Pos)
682+
{
683+
// Check for buffer validity
684+
int64 LocalPos64 = Pos - BufferPos;
685+
if (LocalPos64 < 0 || LocalPos64 >= BufferSize)
686+
{
687+
// Outside of the buffer
688+
BufferBytesLeft = 0;
689+
// SeekPos will be reset to -1 after actual seek
690+
SeekPos = Pos;
691+
}
692+
else
693+
{
694+
// Inside of the buffer, recompute number of bytes to the end
695+
LocalReadPos = (int)LocalPos64;
696+
BufferBytesLeft = BufferSize - LocalReadPos;
697+
}
698+
}
699+
700+
int FFileReader::Tell() const
701+
{
702+
assert((BufferPos >> 32) == 0);
703+
return (int)BufferPos + LocalReadPos;
704+
}
705+
706+
int64 FFileReader::Tell64() const
707+
{
708+
return BufferPos + LocalReadPos;
709+
}
710+
684711
int64 FFileReader::GetFileSize64() const
685712
{
686713
// lazy file size computation
@@ -700,10 +727,17 @@ int64 FFileReader::GetFileSize64() const
700727
return FileSize;
701728
}
702729

730+
bool FFileReader::IsEof() const
731+
{
732+
return (BufferBytesLeft == 0) && (FilePos == GetFileSize64());
733+
}
734+
703735
static TArray<FFileWriter*> GFileWriters;
704736

705737
FFileWriter::FFileWriter(const char *Filename, unsigned InOptions)
706738
: FFileArchive(Filename, InOptions)
739+
, FileSize(0)
740+
, ArPos64(0)
707741
{
708742
guard(FFileWriter::FFileWriter);
709743
IsLoading = false;
@@ -798,6 +832,7 @@ bool FFileWriter::Open()
798832
Buffer = (byte*)appMalloc(FILE_BUFFER_SIZE);
799833
BufferPos = 0;
800834
BufferSize = 0;
835+
ArPos64 = 0;
801836
return OpenFile();
802837
}
803838

@@ -830,11 +865,36 @@ void FFileWriter::FlushBuffer()
830865
}
831866
}
832867

868+
void FFileWriter::Seek(int Pos)
869+
{
870+
ArPos64 = Pos;
871+
}
872+
873+
void FFileWriter::Seek64(int64 Pos)
874+
{
875+
ArPos64 = Pos;
876+
}
877+
878+
int FFileWriter::Tell() const
879+
{
880+
return (int)ArPos64;
881+
}
882+
883+
int64 FFileWriter::Tell64() const
884+
{
885+
return ArPos64;
886+
}
887+
833888
int64 FFileWriter::GetFileSize64() const
834889
{
835890
return max(FileSize, FilePos + BufferSize);
836891
}
837892

893+
bool FFileWriter::IsEof() const
894+
{
895+
return ArPos64 >= GetFileSize64();
896+
}
897+
838898

839899
/*-----------------------------------------------------------------------------
840900
Dummy archive class

umodel.exe

0 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)