This file is indexed.

/usr/lib/lazarus/0.9.30.4/lcl/dynqueue.pas is in lazarus-src-0.9.30.4 0.9.30.4-6.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
{
 *****************************************************************************
 *                                                                           *
 *  This file is part of the Lazarus Component Library (LCL)                 *
 *                                                                           *
 *  See the file COPYING.modifiedLGPL.txt, included in this distribution,    *
 *  for details about the copyright.                                         *
 *                                                                           *
 *  This program is distributed in the hope that it will be useful,          *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                     *
 *                                                                           *
 *****************************************************************************
 
  Abstract:
    A dynamic data queue to push and pop arbitrary data.
}
unit DynQueue;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, LCLProc;
  
type
  TDynamicQueueItem = record
    Size: integer;
    Data: array[0..0] of integer;// type is irrelevant, the record is open ended
  end;
  PDynamicQueueItem = ^TDynamicQueueItem;
  ListOfPDynamicQueueItem = ^PDynamicQueueItem;

  { TDynamicDataQueue
    A queue for arbitrary data. That means first in first out.

    Push: put data in the queue
    Pop:  fetch data from the queue (data is removed from queue)
    Top:  read data in the queue (data remains in the queue)

    This queue maintains internally a ring queue for pointers to data chunks of
    TDynamicQueueItem. It is optimised to reduce the amount of data movement. }

  TDynamicDataQueue =  class
  private
    FItems: ListOfPDynamicQueueItem; // ring queue from FTopIndex to FLastIndex
    FItemCapacity: integer; // length of ListOfPDynamicQueueItem
    FTopIndex: integer; // first item in FItems
    FLastIndex: integer; // last item in FItems
    FMaximumBlockSize: integer;
    FMinimumBlockSize: integer;
    FSize: int64;
    FTopItemSpace: integer; // space in top item
    FLastItemSpace: integer; // remaining space in last item
    procedure SetMaximumBlockSize(const AValue: integer);
    procedure SetMinimumBlockSize(const AValue: integer);
    procedure GrowItems;
    procedure AddItem(ItemSize: integer);
    function CalculateItemSize(ItemSize: integer): integer;
    function PushInternal(Source: PByte; AStream: TStream; Count: integer): integer;// add to end of queue
    function PopTopInternal(Dest: PByte; AStream: TStream; Count: integer; KeepData: Boolean): integer;// read from start of queue, remove from queue
  public
    constructor Create;
    destructor Destroy; override;
    procedure Clear;
    procedure ConsistencyCheck;
    procedure WriteDebugReport(WriteData: Boolean);
    function Push(const Buffer; Count: integer): integer;// add to end of queue
    function Push(AStream: TStream; Count: integer): integer;// add to end of queue
    function Pop(var Buffer; Count: integer): integer; // read from start of queue, remove from queue
    function Pop(AStream: TStream; Count: integer): integer;// read from start of queue, remove from queue
    function Top(var Buffer; Count: integer): integer; // read from start of queue, keep data
    function Top(AStream: TStream; Count: integer): integer;// read from start of queue, keep data
    property Size: int64 read FSize;
    property MinimumBlockSize: integer read FMinimumBlockSize write SetMinimumBlockSize;
    property MaximumBlockSize: integer read FMaximumBlockSize write SetMaximumBlockSize;
  end;

implementation

{ TDynamicDataQueue }

procedure TDynamicDataQueue.SetMinimumBlockSize(const AValue: integer);
begin
  if (FMinimumBlockSize=AValue) then exit;
  FMinimumBlockSize:=AValue;
  if FMinimumBlockSize<16 then FMinimumBlockSize:=16;
  if FMaximumBlockSize<FMinimumBlockSize then
    FMaximumBlockSize:=FMinimumBlockSize;
end;

procedure TDynamicDataQueue.GrowItems;
var
  NewCapacity: LongInt;
  NewSize: Integer;
  NewItems: ListOfPDynamicQueueItem;
  DestIndex: Integer;
  SrcIndex: LongInt;
begin
  // allocate a new ring queue
  NewCapacity:=FItemCapacity;
  if NewCapacity<8 then
    NewCapacity:=8
  else
    NewCapacity:=NewCapacity*2;
  NewSize:=NewCapacity*SizeOf(Pointer);
  GetMem(NewItems,NewSize);
  FillChar(NewItems^,NewSize,0);

  // copy old items
  DestIndex:=0;
  if FItems<>nil then begin
    SrcIndex:=FTopIndex;
    repeat
      NewItems[DestIndex]:=FItems[SrcIndex];
      if SrcIndex=FLastIndex then break;
      inc(DestIndex);
      inc(SrcIndex);
      if SrcIndex=FItemCapacity then
        SrcIndex:=0;
    until false;
    FreeMem(FItems);
  end;
  FTopIndex:=0;
  FLastIndex:=DestIndex;
  FItems:=NewItems;
  FItemCapacity:=NewCapacity;
end;

procedure TDynamicDataQueue.AddItem(ItemSize: integer);
var
  NewIndex: Integer;

  procedure RaiseInconsistency;
  begin
    raise Exception.Create('TDynamicDataQueue.AddItem NewIndex='+IntToStr(NewIndex));
  end;

begin
  // check that there is space for the new item
  NewIndex:=FLastIndex;
  if (FItems<>nil) and (FItems[NewIndex]<>nil) then begin
    inc(NewIndex);
    if NewIndex>=FItemCapacity then
      NewIndex:=0;
  end;
  if NewIndex=FTopIndex then begin
    GrowItems;
    NewIndex:=FLastIndex;
    if FItems[NewIndex]<>nil then begin
      inc(NewIndex);
      if NewIndex>=FItemCapacity then
        NewIndex:=0;
    end;
  end;
  if (FItems=nil) then RaiseInconsistency;
  if (FItems[NewIndex]<>nil) then RaiseInconsistency;
  
  FLastIndex:=NewIndex;
  GetMem(FItems[FLastIndex],SizeOf(TDynamicQueueItem.Size)+ItemSize);
  FItems[FLastIndex]^.Size:=ItemSize;
end;

function TDynamicDataQueue.CalculateItemSize(ItemSize: integer): integer;
begin
  Result:=ItemSize;
  if Result<MinimumBlockSize then
    Result:=MinimumBlockSize;
  if Result>MaximumBlockSize then
    Result:=MaximumBlockSize;
end;

function TDynamicDataQueue.PushInternal(Source: PByte; AStream: TStream;
  Count: integer): integer;
var
  CurCount: PtrInt;
  NewItemSize: LongInt;
  LastItem: PDynamicQueueItem;
  Dest: Pointer;
begin
  Result:=0;
  if Count<=0 then exit;
  while true do begin
    while FLastItemSpace>0 do begin
      // fill the last item
      CurCount:=Count;
      if CurCount>FLastItemSpace then
        CurCount:=FLastItemSpace;
      LastItem:=FItems[FLastIndex];
      Dest:=Pointer(@(LastItem^.Data))+LastItem^.Size-FLastItemSpace;

      // beware: read from a stream can raise an exception
      if Source<>nil then
        System.Move(Source[Result],Dest^,CurCount)
      else
        CurCount:=AStream.Read(Dest^,CurCount);
      if CurCount<=0 then exit;

      // transfer succeeded
      dec(FLastItemSpace,CurCount); // space decreased
      inc(fSize,CurCount);   // Queue increased
      inc(Result,CurCount);  // bytes transferred
      dec(Count,CurCount);   // less to transfer
      if Count=0 then exit;
    end;
    // add new
    NewItemSize:=CalculateItemSize(Count);
    AddItem(NewItemSize);
    FLastItemSpace:=NewItemSize;
  end;
end;

function TDynamicDataQueue.PopTopInternal(Dest: PByte; AStream: TStream;
  Count: integer; KeepData: Boolean): integer;
  
  procedure RaiseInconsistencySizeNot0;
  begin
    raise Exception.Create('TDynamicDataQueue.PopTopInternal inconsistency size<>0');
  end;
  
  procedure RaiseInconsistencyEmptyItem;
  begin
    raise Exception.Create('TDynamicDataQueue.PopTopInternal inconsistency empty item');
  end;
  
  procedure RaiseInconsistencySizeNegative;
  begin
    raise Exception.Create('TDynamicDataQueue.PopTopInternal inconsistency size<0');
  end;
  
var
  Item: PDynamicQueueItem;
  CurCount: Integer;
  Source: PByte;
  CurItemSize: LongInt;
  ReadIndex: LongInt;
  TransferredCount: LongInt;
begin
  Result:=0;
  if Count<=0 then exit;
  ReadIndex:=FTopIndex;

  while Count>0 do begin
    if FItems=nil then exit; // no data
    
    Item:=FItems[ReadIndex];
    CurItemSize:=Item^.Size;
    if ReadIndex=FLastIndex then
      dec(CurItemSize,FLastItemSpace);
    CurCount:=CurItemSize;
    if ReadIndex=FTopIndex then
      dec(CurCount,FTopItemSpace);
    if CurCount<=0 then
      RaiseInconsistencyEmptyItem;
  
    // copy data from the TopItem
    if CurCount>Count then
      CurCount:=Count;
    Source:=PByte(@Item^.Data);
    if ReadIndex=FTopIndex then
      inc(Source,FTopItemSpace);

    // beware: writing to a stream can raise an exception
    if Dest<>nil then begin
      System.Move(Source^,Dest[Result],CurCount);
      TransferredCount:=CurCount;
    end else
      TransferredCount:=AStream.Write(Dest^,CurCount);
    if TransferredCount<=0 then
      exit;
      
    // transfer succeeded (at least partially)
    inc(Result,TransferredCount); // bytes transferred
    dec(Count,TransferredCount);  // less to transfer
    if (not KeepData) then begin
      dec(FSize,TransferredCount);  // Queue decreased
      if FSize<0 then RaiseInconsistencySizeNegative;
      
      if (ReadIndex=FTopIndex) then begin
        inc(FTopItemSpace,TransferredCount); // space in top item increased

        if (FTopItemSpace=CurItemSize) then begin
          // item complete -> remove item
          FreeMem(Item);
          FItems[FTopIndex]:=nil;
          if FTopIndex=FLastIndex then begin
            // complete queue read
            if Size<>0 then RaiseInconsistencySizeNot0;
            Clear;
            exit;
          end;

          FTopItemSpace:=0;
          inc(FTopIndex);
          if FTopIndex=FItemCapacity then FTopIndex:=0;
        end;
      end;
    end;
    if (Count=0) or (TransferredCount<CurCount) then exit;

    if TransferredCount=CurCount then begin
      // next item
      inc(ReadIndex);
      if ReadIndex=FItemCapacity then ReadIndex:=0;
    end;
  end;
end;

procedure TDynamicDataQueue.SetMaximumBlockSize(const AValue: integer);
begin
  if FMaximumBlockSize=AValue then exit;
  FMaximumBlockSize:=AValue;
  if FMaximumBlockSize<FMinimumBlockSize then
    FMaximumBlockSize:=FMinimumBlockSize;
end;

constructor TDynamicDataQueue.Create;
begin
  FMinimumBlockSize:=512;
  FMaximumBlockSize:=4096;
end;

destructor TDynamicDataQueue.Destroy;
begin
  Clear;
  inherited Destroy;
end;

function TDynamicDataQueue.Push(const Buffer; Count: integer): integer;
begin
  Result:=PushInternal(PByte(@Buffer),nil,Count);
end;

function TDynamicDataQueue.Push(AStream: TStream; Count: integer): integer;
begin
  Result:=PushInternal(nil,AStream,Count);
end;

function TDynamicDataQueue.Pop(var Buffer; Count: integer): integer;
begin
  Result:=PopTopInternal(PByte(@Buffer),nil,Count,false);
end;

function TDynamicDataQueue.Pop(AStream: TStream; Count: integer): integer;
begin
  Result:=PopTopInternal(nil,AStream,Count,false);
end;

function TDynamicDataQueue.Top(var Buffer; Count: integer): integer;
begin
  Result:=PopTopInternal(PByte(@Buffer),nil,Count,true);
end;

function TDynamicDataQueue.Top(AStream: TStream; Count: integer): integer;
begin
  Result:=PopTopInternal(nil,AStream,Count,true);
end;

procedure TDynamicDataQueue.Clear;
begin
  while FTopIndex<>FLastIndex do begin
    FreeMem(FItems[FTopIndex]);
    inc(FTopIndex);
    if FTopIndex=FItemCapacity then
      FTopIndex:=0;
  end;
  FTopIndex:=0;
  FLastIndex:=0;
  FSize:=0;
  FreeMem(FItems);
  FItems:=nil;
  FItemCapacity:=0;
  FTopItemSpace:=0;
  FLastItemSpace:=0;
end;

procedure TDynamicDataQueue.ConsistencyCheck;

  procedure Error(const Msg: string);
  begin
    raise Exception.Create('TDynamicDataQueue.ConsistencyCheck '+Msg);
  end;

var
  i: LongInt;
  RealSize: int64;
  CurSize: LongInt;
begin
  if Size<0 then Error('');
  if FMinimumBlockSize>FMaximumBlockSize then Error('');
  if FMinimumBlockSize<16 then Error('');
  if (FItems=nil) then begin
    if Size<>0 then Error('');
  end else begin
    if FItemCapacity<=0 then Error('');
    if Size=0 then Error('');
    if FTopIndex<0 then Error('');
    if FLastIndex<0 then Error('');
    if FTopIndex>=FItemCapacity then Error('');
    if FLastIndex>=FItemCapacity then Error('');
    
    // check used items
    RealSize:=0;
    i:=FTopIndex;
    repeat
      if FItems[i]=nil then Error('');
      if FItems[i]^.Size<=0 then Error('');
      CurSize:=FItems[i]^.Size;
      if FTopIndex=i then
        dec(CurSize,FTopItemSpace);
      if FLastIndex=i then
        dec(CurSize,FLastItemSpace);
      inc(RealSize,CurSize);
      if i=FLastIndex then break;
      inc(i);
      if i=FItemCapacity then i:=0;
    until false;
    if RealSize<>Size then Error('');
    
    // check unused items
    inc(i);
    if i=FItemCapacity then i:=0;
    while (i<>FTopIndex) do begin
      if FItems[i]<>nil then Error('');
      inc(i);
      if i=FItemCapacity then i:=0;
    end;

    // check space
    if FLastItemSpace<0 then Error('');
    if FItems[FLastIndex]^.Size<=FLastItemSpace then Error('');
    if FTopItemSpace<0 then Error('');
    if FItems[FTopIndex]^.Size<=FTopItemSpace then Error('');
    if (FTopIndex=FLastIndex)
    and (FTopItemSpace>=FItems[FTopIndex]^.Size-FLastItemSpace) then Error('');
  end;
end;

procedure TDynamicDataQueue.WriteDebugReport(WriteData: Boolean);
var
  i: LongInt;
  DataCount: LongInt;
  DataOffset: Integer;
begin
  debugln(['TDynamicDataQueue.WriteDebugReport FItemCapacity=',FItemCapacity,
    ' FTopIndex=',FTopIndex,' FTopItemSpace=',FTopItemSpace,
    ' FLastIndex=',FLastIndex,' FLastItemSpace=',FLastItemSpace,
    ' Size=',Size,
    ' MinimumBlockSize=',MinimumBlockSize,
    ' MaximumBlockSize=',MaximumBlockSize]);
  if FItems<>nil then begin
    i:=FTopIndex;
    repeat
      DataCount:=FItems[i]^.Size;
      DataOffset:=0;
      if FTopIndex=i then begin
        dec(DataCount,FTopItemSpace);
        inc(DataOffset,FTopItemSpace);
      end;
      if i=FLastIndex then
        dec(DataCount,FLastItemSpace);
      debugln([i,' Item=',HexStr(PtrUInt(FItems[i]),8),' Size=',fItems[i]^.Size,' Start=',DataOffset,' Count=',DataCount]);
      if WriteData then begin
        debugln(dbgMemRange(PByte(@FItems[i]^.Data)+DataOffset,DataCount));
      end;
      
      if i=FLastIndex then break;
      inc(i);
      if i=FItemCapacity then i:=0;
    until false;
  end;
end;

end.