1+ import { FAT } from "../alloc/FAT" ;
2+ import { MiniFAT } from "../alloc/MiniFAT" ;
3+ import { Header } from "../Header" ;
4+ import { Sectors } from "../Sectors" ;
5+ import { ENDOFCHAIN_MARK_INT , FREESECT_MARK_OR_NOSTREAM_INT , initializedWith } from "../utils" ;
6+ import { VariableSizeChunkedDataView } from "../dataview/VarSizeChunkedDataview" ;
7+ import { CFDataview } from "../dataview/СFDataview" ;
8+ import { Sector } from "../dataview/Sector" ;
9+ import { StreamRW } from "./StreamRW" ;
10+
11+ export class MiniStreamRW implements StreamRW {
12+
13+ public static readonly MINI_STREAM_CHUNK_SIZE = 64 ;
14+ private readonly miniFAT : MiniFAT ;
15+ private readonly header : Header ;
16+ private miniStreamLength : number ;
17+ private readonly fat : FAT ;
18+ private readonly miniStreamSectorChain : number [ ] ;
19+ private readonly sectors : Sectors ;
20+
21+ constructor ( miniFAT : MiniFAT , fat : FAT , firstMiniStreamSector : number , miniStreamLength : number , sectors : Sectors , header : Header ) {
22+ this . miniFAT = miniFAT ;
23+ this . fat = fat ;
24+ this . miniStreamLength = miniStreamLength ;
25+ if ( firstMiniStreamSector >= 0 ) {
26+ this . miniStreamSectorChain = fat . buildChain ( firstMiniStreamSector ) ;
27+ } else {
28+ this . miniStreamSectorChain = [ ] ;
29+ }
30+ this . sectors = sectors ;
31+ this . header = header ;
32+ }
33+
34+ read ( startingSector : number , lengthOrFromIncl : number , toExcl ?: number ) : number [ ] {
35+ if ( toExcl == null ) {
36+ const result = initializedWith ( lengthOrFromIncl , 0 ) ;
37+ let position = 0 ;
38+ for ( const sectorNumber of this . miniFAT . buildChain ( startingSector ) ) {
39+ if ( lengthOrFromIncl > 0 ) {
40+ const data = this . getMiniSectorData ( sectorNumber ) ;
41+ const bytesToRead = Math . min ( data . getSize ( ) , lengthOrFromIncl ) ;
42+ result . splice ( position , bytesToRead , ...data . subView ( 0 , bytesToRead ) . getData ( ) ) ;
43+ position += bytesToRead ;
44+ lengthOrFromIncl -= bytesToRead ;
45+ } else {
46+ break ;
47+ }
48+ }
49+ return result ;
50+ } else {
51+ return new VariableSizeChunkedDataView ( this . miniFAT . buildChain ( startingSector ) . map ( ( position ) => this . getMiniSectorData ( position ) ) )
52+ . subView ( lengthOrFromIncl , toExcl ) . getData ( ) ;
53+ }
54+ }
55+
56+ getMiniSectorData ( position : number ) : CFDataview {
57+ const sectorPosition = position * Math . floor ( this . header . getMiniSectorShift ( ) / this . header . getSectorShift ( ) ) ;
58+ const shiftInsideSector = position * this . header . getMiniSectorShift ( ) % this . header . getSectorShift ( ) ;
59+ return this . sectors . sector ( this . miniStreamSectorChain [ sectorPosition ] ) . subView ( shiftInsideSector , shiftInsideSector + this . header . getMiniSectorShift ( ) ) ;
60+ }
61+
62+ write ( data : number [ ] ) : number {
63+ if ( data . length <= 0 ) {
64+ throw new Error ( ) ;
65+ }
66+ const numberOfChunks = this . howManyChunksNeeded ( data . length ) ;
67+ let firstMiniSectorPosition = ENDOFCHAIN_MARK_INT ;
68+ for ( let i = 0 ; i < numberOfChunks ; i ++ ) {
69+ const bytesFromPosition = i * this . header . getMiniSectorShift ( ) ;
70+ const bytesUpToPosition = Math . min ( ( i + 1 ) * this . header . getMiniSectorShift ( ) , data . length ) ;
71+ const bytesToWrite = data . slice ( bytesFromPosition , bytesUpToPosition ) ;
72+ this . getDataHolderForNextChunk ( ) . writeAt ( 0 , bytesToWrite ) ;
73+ const miniSectorPosition = this . miniStreamLength / this . header . getMiniSectorShift ( ) ;
74+ if ( firstMiniSectorPosition === ENDOFCHAIN_MARK_INT ) {
75+ firstMiniSectorPosition = miniSectorPosition ;
76+ }
77+ if ( i === 0 ) {
78+ this . miniFAT . registerSector ( miniSectorPosition , null ) ;
79+ } else {
80+ this . miniFAT . registerSector ( miniSectorPosition , miniSectorPosition - 1 ) ;
81+ }
82+ this . miniStreamLength += this . header . getMiniSectorShift ( ) ;
83+ }
84+ return firstMiniSectorPosition ;
85+ }
86+
87+ howManyChunksNeeded ( dataLength : number ) : number {
88+ let numberOfChunks ;
89+ if ( dataLength % this . header . getMiniSectorShift ( ) === 0 ) {
90+ numberOfChunks = Math . floor ( dataLength / this . header . getMiniSectorShift ( ) ) ;
91+ } else {
92+ numberOfChunks = Math . floor ( dataLength / this . header . getMiniSectorShift ( ) ) + 1 ;
93+ }
94+ return numberOfChunks ;
95+ }
96+
97+ writeAt ( startingSector : number , position : number , data : number [ ] ) : void {
98+ new VariableSizeChunkedDataView ( this . miniFAT . buildChain ( startingSector ) . map ( ( pos ) => this . getMiniSectorData ( pos ) ) )
99+ . writeAt ( position , data ) ;
100+ }
101+
102+ append ( startingSector : number , currentSize : number , data : number [ ] ) : number {
103+ const sectorChain = this . miniFAT . buildChain ( startingSector ) ;
104+ if ( sectorChain . length === 0 ) {
105+ return this . write ( data ) ;
106+ }
107+ const lastSectorPosition = sectorChain [ sectorChain . length - 1 ] ;
108+ const lastSector = this . getMiniSectorData ( lastSectorPosition ) ;
109+ let freeBytesInLastSector = 0 ;
110+ let remainingBytes = data . length ;
111+ if ( currentSize % this . header . getMiniSectorShift ( ) !== 0 ) {
112+ freeBytesInLastSector = lastSector . getSize ( ) - currentSize % this . header . getMiniSectorShift ( ) ;
113+ if ( freeBytesInLastSector > 0 ) {
114+ const byteToWrite = Math . min ( freeBytesInLastSector , data . length ) ;
115+ lastSector . writeAt ( lastSector . getSize ( ) - freeBytesInLastSector , data . slice ( 0 , byteToWrite ) ) ;
116+ freeBytesInLastSector -= byteToWrite ;
117+ remainingBytes -= byteToWrite ;
118+ }
119+ }
120+ if ( freeBytesInLastSector > 0 || remainingBytes === 0 ) {
121+ return startingSector ;
122+ }
123+ const numberOfChunks = this . howManyChunksNeeded ( remainingBytes ) ;
124+ for ( let i = 0 ; i < numberOfChunks ; i ++ ) {
125+ const bytesFromPosition = i * this . header . getMiniSectorShift ( ) ;
126+ const bytesUpToPosition = Math . min ( ( i + 1 ) * this . header . getMiniSectorShift ( ) , data . length ) ;
127+ const bytesToWrite = data . slice ( bytesFromPosition , bytesUpToPosition ) ;
128+ this . getDataHolderForNextChunk ( ) . writeAt ( 0 , bytesToWrite ) ;
129+ const miniSectorPosition = this . miniStreamLength / this . header . getMiniSectorShift ( ) ;
130+ if ( i === 0 ) {
131+ this . miniFAT . registerSector ( miniSectorPosition , lastSectorPosition ) ;
132+ } else {
133+ this . miniFAT . registerSector ( miniSectorPosition , miniSectorPosition - 1 ) ;
134+ }
135+ this . miniStreamLength += this . header . getMiniSectorShift ( ) ;
136+ }
137+ return startingSector ;
138+ }
139+
140+ getDataHolderForNextChunk ( ) : CFDataview {
141+ const currentSector = this . getSectorForNextChunk ( ) ;
142+ const positionInCurrentSector = this . miniStreamLength % this . header . getSectorShift ( ) ;
143+ return currentSector . subView ( positionInCurrentSector , positionInCurrentSector + this . header . getMiniSectorShift ( ) ) ;
144+ }
145+
146+ getSectorForNextChunk ( ) : Sector {
147+ if ( this . miniStreamSectorChain . length === 0 ) {
148+ const sector = this . sectors . allocate ( ) ;
149+ this . fat . registerSector ( sector . getPosition ( ) , null ) ;
150+ this . miniStreamSectorChain . push ( sector . getPosition ( ) ) ;
151+ return sector ;
152+ } else if ( this . miniStreamLength % this . header . getSectorShift ( ) === 0 ) {
153+ const sector = this . sectors . allocate ( ) ;
154+ this . fat . registerSector ( sector . getPosition ( ) , this . sectors . sector ( this . miniStreamSectorChain [ this . miniStreamSectorChain . length - 1 ] ) . getPosition ( ) ) ;
155+ this . miniStreamSectorChain . push ( sector . getPosition ( ) ) ;
156+ return sector ;
157+ } else {
158+ return this . sectors . sector ( this . miniStreamSectorChain [ this . miniStreamSectorChain . length - 1 ] ) ;
159+ }
160+ }
161+
162+ getMiniStreamLength ( ) : number {
163+ return this . miniStreamLength ;
164+ }
165+
166+ getMiniStreamFirstSectorPosition ( ) : number {
167+ return this . miniStreamLength <= 0 ? FREESECT_MARK_OR_NOSTREAM_INT : this . miniStreamSectorChain [ 0 ] ;
168+ }
169+ }
0 commit comments