Merge
authorraptor <buckyballreaction@gmail.com>
Mon, 26 Aug 2019 15:44:03 -0600
changeset 15368 c5934cdeecb6
parent 15365 34eb5cc72241 (diff)
parent 15367 c8f37fc9c266 (current diff)
child 15369 114b036522a6
Merge
hedgewars/uRender.pas
--- a/hedgewars/uAIMisc.pas	Mon Aug 26 15:40:15 2019 -0600
+++ b/hedgewars/uAIMisc.pas	Mon Aug 26 15:44:03 2019 -0600
@@ -799,7 +799,7 @@
                         begin
                         dead:= true;
                         Targets.reset:= true;
-                        if dX < 0.035 then
+                        if gdX < 0.035 then
                             begin
                             subrate:= RealRateExplosion(Me, round(pX), round(pY), 61, afErasesLand or afTrackFall);
                             if abs(subrate) > 2000 then inc(Rate,subrate div 1024)
--- a/hedgewars/uConsts.pas	Mon Aug 26 15:40:15 2019 -0600
+++ b/hedgewars/uConsts.pas	Mon Aug 26 15:44:03 2019 -0600
@@ -110,6 +110,8 @@
     ifCritical    = $00000002;  // image is critical for gameplay (exit game if unable to load)
     ifColorKey    = $00000004;  // image uses transparent pixels (color keying)
     ifIgnoreCaps  = $00000008;  // ignore hardware capabilities when loading (i.e. image will not be drawn using OpenGL)
+    ifDigestAlpha = $00000010;  // add alpha channel to the digest
+    ifDigestAll   = $00000020;  // add all channels to the digest
 
     // texture priority (allows OpenGL to keep frequently used textures in video memory more easily)
     tpLowest      = 0.00;
--- a/hedgewars/uGears.pas	Mon Aug 26 15:40:15 2019 -0600
+++ b/hedgewars/uGears.pas	Mon Aug 26 15:44:03 2019 -0600
@@ -276,7 +276,7 @@
             end;
         if curHandledGear^.Active then
             begin
-            if curHandledGear^.RenderTimer then
+            if (not cOnlyStats) and curHandledGear^.RenderTimer then
                 begin
                 // Mine timer
                 if (curHandledGear^.Kind in [gtMine, gtSMine, gtAirMine]) then
--- a/hedgewars/uGearsHandlersMess.pas	Mon Aug 26 15:40:15 2019 -0600
+++ b/hedgewars/uGearsHandlersMess.pas	Mon Aug 26 15:44:03 2019 -0600
@@ -2422,7 +2422,7 @@
         doStepCase(Gear)
     else
         // health texture (FlightTime = health when the last texture was generated)
-        if (Gear^.Health <> Gear^.FlightTime) or (Gear^.Tex = nil) then
+        if (not cOnlyStats) and ((Gear^.Health <> Gear^.FlightTime) or (Gear^.Tex = nil)) then
             begin
             Gear^.FlightTime:= Gear^.Health;
             FreeAndNilTexture(Gear^.Tex);
@@ -2500,7 +2500,7 @@
         dec(Gear^.Health, Gear^.Damage);
         Gear^.Damage := 0;
         // health texture (FlightTime = health when the last texture was generated)
-        if (Gear^.Health <> Gear^.FlightTime) or (Gear^.Tex = nil) then
+        if (not cOnlyStats) and ((Gear^.Health <> Gear^.FlightTime) or (Gear^.Tex = nil)) then
             begin
             Gear^.FlightTime:= Gear^.Health;
             FreeAndNilTexture(Gear^.Tex);
@@ -2523,12 +2523,14 @@
                         i:= 1
                     else
                         i:= 0
+				else if cOnlyStats then
+					i:= 0
                 // Always show health (default)
                 else
                     i:= 1;
                 if i = 1 then
                     begin
-                    if (Gear^.Health <> Gear^.FlightTime) or (Gear^.Tex = nil) then
+                    if (not cOnlyStats) and ((Gear^.Health <> Gear^.FlightTime) or (Gear^.Tex = nil)) then
                         begin
                         Gear^.FlightTime:= Gear^.Health;
                         FreeAndNilTexture(Gear^.Tex);
@@ -2537,7 +2539,7 @@
                     end
                 else
                     begin
-                    if (Gear^.FlightTime <> $ffffffff) or (Gear^.Tex = nil) then
+                    if (not cOnlyStats) and ((Gear^.FlightTime <> $ffffffff) or (Gear^.Tex = nil)) then
                         begin
                         Gear^.FlightTime:= $ffffffff;
                         FreeAndNilTexture(Gear^.Tex);
@@ -4615,7 +4617,7 @@
 
     i:= Gear^.Health div 20;
 
-    if (i <> Gear^.Damage) and ((GameTicks and $3F) = 0) then
+    if (not cOnlyStats) and (i <> Gear^.Damage) and ((GameTicks and $3F) = 0) then
         begin
         Gear^.Damage:= i;
         FreeAndNilTexture(Gear^.Tex);
@@ -5887,7 +5889,7 @@
     else
         begin
         i:= Gear^.Health div 5;
-        if (i <> Gear^.Damage) and ((GameTicks and $3F) = 0) then
+        if (not cOnlyStats) and (i <> Gear^.Damage) and ((GameTicks and $3F) = 0) then
             begin
             Gear^.Damage:= i;
             FreeAndNilTexture(Gear^.Tex);
@@ -5978,7 +5980,7 @@
     else
         begin
         i:= Gear^.Health div 10;
-        if (i <> Gear^.Damage) and ((GameTicks and $3F) = 0) then
+        if (not cOnlyStats) and (i <> Gear^.Damage) and ((GameTicks and $3F) = 0) then
             begin
             Gear^.Damage:= i;
             FreeAndNilTexture(Gear^.Tex);
@@ -6497,7 +6499,7 @@
   t:LongInt;
 begin
     t:= Gear^.Health div 10;
-    if (t <> Gear^.Damage) and ((GameTicks and $3F) = 0) then
+    if (not cOnlyStats) and (t <> Gear^.Damage) and ((GameTicks and $3F) = 0) then
     begin
     Gear^.Damage:= t;
     FreeAndNilTexture(Gear^.Tex);
--- a/hedgewars/uLandObjects.pas	Mon Aug 26 15:40:15 2019 -0600
+++ b/hedgewars/uLandObjects.pas	Mon Aug 26 15:44:03 2019 -0600
@@ -36,7 +36,7 @@
 implementation
 uses uStore, uConsts, uConsole, uRandom, uSound
      , uTypes, uVariables, uDebug, uUtils
-     , uPhysFSLayer, adler32, uRenderUtils;
+     , uPhysFSLayer, uRenderUtils;
 
 const MaxRects = 512;
       MAXOBJECTRECTS = 16;
@@ -346,51 +346,13 @@
     CountNonZeroz:= lRes;
 end;
 
-procedure ChecksumLandObjectImage(Image: PSDL_Surface; alphaOnly: boolean);
-var y, x: LongInt;
-var rowData: PByteArray;
-begin
-    if Image = nil then exit;
-
-    if alphaOnly then
-        rowData := GetMem(Image^.w);
-
-    if SDL_MustLock(Image) then
-        SDL_LockSurface(Image);
-
-    if checkFails(Image^.format^.BytesPerPixel = 4, 'Land object image should be 32bit', true) then
-    begin
-        if SDL_MustLock(Image) then
-            SDL_UnlockSurface(Image);
-        exit
-    end;
-
-    for y := 0 to Image^.h - 1 do
-        if alphaOnly then
-            begin
-            for x := 0 to Image^.w - 1 do
-                rowData^[x] := PByteArray(Image^.pixels)^[y * Image^.pitch + x * 4 + AByteIndex];
-            syncedPixelDigest:= Adler32Update(syncedPixelDigest, rowData, Image^.w);
-            end
-        else
-            syncedPixelDigest:= Adler32Update(syncedPixelDigest, @PByteArray(Image^.pixels)^[y*Image^.pitch], Image^.w*4);
-
-    if SDL_MustLock(Image) then
-        SDL_UnlockSurface(Image);
-
-    if alphaOnly then
-        FreeMem(rowData, Image^.w);
-end;
-
 function AddGirder(gX: LongInt; var girSurf: PSDL_Surface): boolean;
 var x1, x2, y, k, i, girderHeight: LongInt;
     rr: TSDL_Rect;
     bRes: boolean;
 begin
 if girSurf = nil then
-    girSurf:= LoadDataImageAltPath(ptCurrTheme, ptGraphics, 'Girder', ifCritical or ifColorKey or ifIgnoreCaps);
-
-ChecksumLandObjectImage(girsurf, true);
+    girSurf:= LoadDataImageAltPath(ptCurrTheme, ptGraphics, 'Girder', ifCritical or ifColorKey or ifIgnoreCaps or ifDigestAlpha);
 
 girderHeight:= girSurf^.h;
 
@@ -745,11 +707,10 @@
     Delete(s, 1, i);
     i:= Pos(',', s);
     if i = 0 then i:= Succ(Length(S));
-    Surf:= LoadDataImage(ptCurrTheme, Trim(Copy(s, 1, Pred(i))), ifColorKey or ifIgnoreCaps or ifCritical);
+    Surf:= LoadDataImage(ptCurrTheme, Trim(Copy(s, 1, Pred(i))), ifColorKey or ifIgnoreCaps or ifCritical or ifDigestAlpha );
     Width:= Surf^.w;
     Height:= Surf^.h;
     Delete(s, 1, i);
-    ChecksumLandObjectImage(Surf, true);
     end;
 end;
 
@@ -949,19 +910,22 @@
             begin
             i:= Pos(',', s);
             Name:= Trim(Copy(s, 1, Pred(i)));
-            Surf:= LoadDataImage(ptCurrTheme, Name, ifColorKey or ifIgnoreCaps or ifCritical);
+
+            Mask:= LoadDataImage(ptCurrTheme, Trim(Copy(s, 1, Pred(i)))+'_mask', ifColorKey or ifIgnoreCaps or ifDigestAll);
+            if Mask = nil then
+                Surf:= LoadDataImage(ptCurrTheme, Name, ifColorKey or ifIgnoreCaps or ifCritical or ifDigestAlpha)
+            else
+                Surf:= LoadDataImage(ptCurrTheme, Name, ifColorKey or ifIgnoreCaps or ifCritical);
+
             Width:= Surf^.w;
             Height:= Surf^.h;
-            Mask:= LoadDataImage(ptCurrTheme, Trim(Copy(s, 1, Pred(i)))+'_mask', ifColorKey or ifIgnoreCaps);
+
             Delete(s, 1, i);
             i:= Pos(',', s);
             Maxcnt:= StrToInt(Trim(Copy(s, 1, Pred(i))));
             Delete(s, 1, i);
             if (Maxcnt < 1) or (Maxcnt > MAXTHEMEOBJECTS) then
                 OutError('Broken theme. Object''s max. count should be between 1 and '+ inttostr(MAXTHEMEOBJECTS) +' (it was '+ inttostr(Maxcnt) +').', true);
-            if Mask = nil then
-                ChecksumLandObjectImage(Surf, true);
-            ChecksumLandObjectImage(Mask, false);
 
             inrectcnt := 0;
 
--- a/hedgewars/uRender.pas	Mon Aug 26 15:40:15 2019 -0600
+++ b/hedgewars/uRender.pas	Mon Aug 26 15:44:03 2019 -0600
@@ -2177,6 +2177,7 @@
 
 procedure freeModule;
 begin
+    if cOnlyStats then exit;
 {$IFDEF GL2}
     glDeleteProgram(shaderMain);
     glDeleteProgram(shaderWater);
--- a/hedgewars/uStore.pas	Mon Aug 26 15:40:15 2019 -0600
+++ b/hedgewars/uStore.pas	Mon Aug 26 15:44:03 2019 -0600
@@ -385,8 +385,7 @@
 var ii: TSprite;
     ai: TAmmoType;
     tmpsurf, tmpoverlay: PSDL_Surface;
-    i, y, x, imflags: LongInt;
-    rowData: PByteArray;
+    i, imflags: LongInt;
     keyConfirm, keyQuit: shortstring;
 begin
 AddFileLog('StoreLoad()');
@@ -431,18 +430,10 @@
                     imflags := (imflags or ifCritical);
 
                 // load the image
-                tmpsurf := LoadDataImageAltPath(Path, AltPath, FileName, imflags);
-                if (tmpsurf <> nil) and checkSum then
-                    begin
-                    rowData := GetMem(tmpsurf^.w);
-                    for y := 0 to tmpsurf^.h-1 do
-                        begin
-                        for x := 0 to tmpsurf^.w - 1 do
-                            rowData^[x] := PByteArray(tmpsurf^.pixels)^[y * tmpsurf^.pitch + x * 4 + AByteIndex];
-                        syncedPixelDigest:= Adler32Update(syncedPixelDigest, rowData, tmpsurf^.w);
-                        end;
-                    FreeMem(rowData, tmpsurf^.w);
-                    end;
+                if checkSum then
+                    tmpsurf := LoadDataImageAltPath(Path, AltPath, FileName, imflags or ifDigestAlpha)
+                else
+                    tmpsurf := LoadDataImageAltPath(Path, AltPath, FileName, imflags);
                 end;
 
             if tmpsurf <> nil then
@@ -646,10 +637,13 @@
 function LoadImage(const filename: shortstring; imageFlags: LongInt): PSDL_Surface;
 var tmpsurf: PSDL_Surface;
     s: shortstring;
-    logMsg: shortstring;
+    logMsg, digestMsg: shortstring;
     rwops: PSDL_RWops;
+    y, x: LongInt;
+    rowData: PByteArray;
 begin
     LoadImage:= nil;
+    digestMsg:= '';
     logMsg:= msgLoading + filename + '.png [flags: ' + inttostr(imageFlags) + ']';
 
     s:= filename + '.png';
@@ -700,8 +694,37 @@
     if (imageFlags and ifColorKey) <> 0 then
         if checkFails(SDL_SetColorKey(tmpsurf, SDL_TRUE, 0) = 0, errmsgTransparentSet, true) then exit;
 
+    if ((imageFlags and (ifDigestAll or ifDigestAlpha)) <> 0)
+        and (tmpsurf^.format^.BytesPerPixel = 4)then
+        begin
+        if SDL_MustLock(tmpsurf) then
+            SDL_LockSurface(tmpsurf);
+
+        if (imageFlags and ifDigestAll) <> 0 then
+            begin
+            for y := 0 to tmpsurf^.h - 1 do
+                syncedPixelDigest:= Adler32Update(syncedPixelDigest, @PByteArray(tmpsurf^.pixels)^[y*tmpsurf^.pitch], tmpsurf^.w*4);
+            digestMsg := ' [CD: ' + inttostr(syncedPixelDigest) + ']'
+            end
+        else if (imageFlags and ifDigestAlpha) <> 0 then
+            begin
+            rowData := GetMem(tmpsurf^.w);
+            for y := 0 to tmpsurf^.h - 1 do
+                begin
+                for x := 0 to tmpsurf^.w - 1 do
+                    rowData^[x] := PByteArray(tmpsurf^.pixels)^[y * tmpsurf^.pitch + x * 4 + AByteIndex];
+                syncedPixelDigest:= Adler32Update(syncedPixelDigest, rowData, tmpsurf^.w);
+                end;
+            FreeMem(rowData, tmpsurf^.w);
+            digestMsg := ' [AD: ' + inttostr(syncedPixelDigest) + ']'
+            end;
+
+        if SDL_MustLock(tmpsurf) then
+            SDL_UnlockSurface(tmpsurf);
+        end;
+
     // log success
-    WriteLnToConsole(logMsg + ' ' + msgOK + ' (' + inttostr(tmpsurf^.w) + 'x' + inttostr(tmpsurf^.h) + ')');
+    WriteLnToConsole(logMsg + ' ' + msgOK + ' (' + inttostr(tmpsurf^.w) + 'x' + inttostr(tmpsurf^.h) + ')' + digestMsg);
 
     LoadImage:= tmpsurf //Result
 end;
--- a/rust/hwphysics/src/data.rs	Mon Aug 26 15:40:15 2019 -0600
+++ b/rust/hwphysics/src/data.rs	Mon Aug 26 15:44:03 2019 -0600
@@ -3,19 +3,19 @@
     any::TypeId,
     mem::{size_of, MaybeUninit},
     num::NonZeroU16,
-    ptr::NonNull,
+    ptr::{copy_nonoverlapping, NonNull},
     slice,
 };
 
-pub trait TypeTuple: Sized {
+pub unsafe trait TypeTuple: Sized {
     fn len() -> usize;
     fn get_types(dest: &mut Vec<TypeId>);
-    unsafe fn iter<F>(slices: &[NonNull<u8>], count: usize, f: F)
+    unsafe fn iter<F>(slices: &[NonNull<u8>], count: usize, mut f: F)
     where
-        F: Fn(Self);
+        F: FnMut(Self);
 }
 
-impl<T: 'static> TypeTuple for (&T,) {
+unsafe impl<T: 'static> TypeTuple for (&T,) {
     fn len() -> usize {
         1
     }
@@ -24,9 +24,9 @@
         dest.push(TypeId::of::<T>());
     }
 
-    unsafe fn iter<F>(slices: &[NonNull<u8>], count: usize, f: F)
+    unsafe fn iter<F>(slices: &[NonNull<u8>], count: usize, mut f: F)
     where
-        F: Fn(Self),
+        F: FnMut(Self),
     {
         let slice1 = slice::from_raw_parts(slices[0].as_ptr() as *const T, count);
         for i in 0..count {
@@ -41,7 +41,7 @@
     max_elements: u16,
     elements_count: u16,
     data: Box<[u8; BLOCK_SIZE]>,
-    blocks: [Option<NonNull<u8>>; 64],
+    component_blocks: [Option<NonNull<u8>>; 64],
 }
 
 impl Unpin for DataBlock {}
@@ -51,18 +51,18 @@
         let total_size: u16 = element_sizes
             .iter()
             .enumerate()
-            .filter(|(i, _)| mask & (164 << *i as u64) != 0)
+            .filter(|(i, _)| mask & (1 << *i as u64) != 0)
             .map(|(_, size)| *size)
             .sum();
         let max_elements = (BLOCK_SIZE / total_size as usize) as u16;
 
         let mut data: Box<[u8; BLOCK_SIZE]> =
-            Box::new(unsafe { std::mem::MaybeUninit::uninit().assume_init() });
+            Box::new(unsafe { MaybeUninit::uninit().assume_init() });
         let mut blocks = [None; 64];
         let mut offset = 0;
 
         for i in 0..64 {
-            if mask & (164 << i) != 0 {
+            if mask & (1 << i) != 0 {
                 blocks[i] = Some(NonNull::new(data[offset..].as_mut_ptr()).unwrap());
                 offset += element_sizes[i] as usize * max_elements as usize;
             }
@@ -71,7 +71,7 @@
             elements_count: 0,
             max_elements,
             data,
-            blocks,
+            component_blocks: blocks,
         }
     }
 
@@ -112,27 +112,70 @@
     }
 
     fn move_between_blocks(&mut self, from_block_index: u16, from_index: u16, to_block_index: u16) {
+        debug_assert!(from_block_index != to_block_index);
         let source_mask = self.block_masks[from_block_index as usize];
         let destination_mask = self.block_masks[to_block_index as usize];
         debug_assert!(source_mask & destination_mask == source_mask);
 
         let source = &self.blocks[from_block_index as usize];
         let destination = &self.blocks[to_block_index as usize];
+        debug_assert!(from_index < source.elements_count);
+        debug_assert!(!destination.is_full());
+
         for i in 0..64 {
-            unimplemented!()
+            if source_mask & 1 << i != 0 {
+                unsafe {
+                    copy_nonoverlapping(
+                        source.component_blocks[i].unwrap().as_ptr(),
+                        destination.component_blocks[i].unwrap().as_ptr(),
+                        self.element_sizes[i] as usize,
+                    );
+                }
+            }
         }
+        self.blocks[from_block_index as usize].elements_count -= 1;
+        self.blocks[to_block_index as usize].elements_count += 1;
     }
 
-    fn add_to_block<T>(&mut self, block_index: u16, value: &T) {
-        unimplemented!()
+    fn add_to_block<T: Clone>(&mut self, block_index: u16, value: &T) {
+        debug_assert!(self.block_masks[block_index as usize].count_ones() == 1);
+
+        let block = &mut self.blocks[block_index as usize];
+        debug_assert!(block.elements_count < block.max_elements);
+
+        unsafe {
+            let slice = slice::from_raw_parts_mut(
+                block.data.as_mut_ptr() as *mut T,
+                block.max_elements as usize,
+            );
+            *slice.get_unchecked_mut(block.elements_count as usize) = value.clone();
+        };
+        block.elements_count += 1;
     }
 
     fn remove_from_block(&mut self, block_index: u16, index: u16) {
-        unimplemented!()
+        let block = &mut self.blocks[block_index as usize];
+        debug_assert!(index < block.elements_count);
+
+        for (i, size) in self.element_sizes.iter().cloned().enumerate() {
+            if index < block.elements_count - 1 {
+                if let Some(ptr) = block.component_blocks[i] {
+                    unsafe {
+                        copy_nonoverlapping(
+                            ptr.as_ptr()
+                                .add((size * (block.elements_count - 1)) as usize),
+                            ptr.as_ptr().add((size * index) as usize),
+                            size as usize,
+                        );
+                    }
+                }
+            }
+        }
+        block.elements_count -= 1;
     }
 
     #[inline]
-    fn ensure_group(&mut self, mask: u64) -> u16 {
+    fn ensure_block(&mut self, mask: u64) -> u16 {
         if let Some(index) = self
             .block_masks
             .iter()
@@ -142,13 +185,14 @@
             index as u16
         } else {
             self.blocks.push(DataBlock::new(mask, &self.element_sizes));
+            self.block_masks.push(mask);
             (self.blocks.len() - 1) as u16
         }
     }
 
     pub fn add<T: Clone + 'static>(&mut self, gear_id: GearId, value: &T) {
         if let Some(type_index) = self.get_type_index::<T>() {
-            let type_bit = 1u64 << type_index as u64;
+            let type_bit = 1 << type_index as u64;
             let entry = self.lookup[gear_id.get() as usize - 1];
 
             if let Some(index) = entry.index {
@@ -156,11 +200,11 @@
                 let new_mask = mask | type_bit;
 
                 if new_mask != mask {
-                    let dest_block_index = self.ensure_group(new_mask);
+                    let dest_block_index = self.ensure_block(new_mask);
                     self.move_between_blocks(entry.block_index, index.get() - 1, dest_block_index);
                 }
             } else {
-                let dest_block_index = self.ensure_group(type_bit);
+                let dest_block_index = self.ensure_block(type_bit);
                 self.add_to_block(dest_block_index, value);
             }
         } else {
@@ -172,15 +216,36 @@
         if let Some(type_index) = self.get_type_index::<T>() {
             let entry = self.lookup[gear_id.get() as usize - 1];
             if let Some(index) = entry.index {
-                self.remove_from_block(entry.block_index, index.get() - 1);
+                let destination_mask =
+                    self.block_masks[entry.block_index as usize] & !(1 << type_index as u64);
+
+                if destination_mask == 0 {
+                    self.remove_all(gear_id)
+                } else {
+                    let destination_block_index = self.ensure_block(destination_mask);
+                    self.move_between_blocks(
+                        entry.block_index,
+                        index.get() - 1,
+                        destination_block_index,
+                    );
+                }
             }
+        } else {
+            panic!("Unregistered type")
+        }
+    }
+
+    pub fn remove_all(&mut self, gear_id: GearId) {
+        let entry = self.lookup[gear_id.get() as usize - 1];
+        if let Some(index) = entry.index {
+            self.remove_from_block(entry.block_index, index.get() - 1);
         }
     }
 
     pub fn register<T: 'static>(&mut self) {
-        assert!(!std::mem::needs_drop::<T>());
-        assert!(self.types.len() <= 64);
-        assert!(size_of::<T>() <= u16::max_value() as usize);
+        debug_assert!(!std::mem::needs_drop::<T>());
+        debug_assert!(self.types.len() <= 64);
+        debug_assert!(size_of::<T>() <= u16::max_value() as usize);
 
         let id = TypeId::of::<T>();
         if !self.types.contains(&id) {
@@ -190,26 +255,38 @@
     }
 
     fn create_selector(&self, types: &[TypeId]) -> u64 {
-        let mut selector = 0u64;
+        let mut selector = 0;
         for (i, typ) in self.types.iter().enumerate() {
             if types.contains(&typ) {
-                selector |= 1u64 << (i as u64)
+                selector |= 1 << (i as u64)
             }
         }
         selector
     }
 
-    pub fn iter<T: TypeTuple + 'static, F: Fn(T) + Copy>(&self, f: F) {
+    pub fn iter<T: TypeTuple + 'static, F: FnMut(T)>(&self, mut f: F) {
         let mut types = vec![];
         T::get_types(&mut types);
-        let types_count = types.len();
+        debug_assert!(types.iter().all(|t| self.types.contains(t)));
 
+        let types_count = types.len();
         let selector = self.create_selector(&types);
+
+        let mut slices = Vec::with_capacity(64);
+
         for (block_index, mask) in self.block_masks.iter().enumerate() {
             if mask & selector == selector {
+                slices.clear();
                 let block = &self.blocks[block_index];
-                for element_index in 0..block.max_elements {
-                    unimplemented!()
+
+                for (i, ptr_opt) in block.component_blocks.iter().cloned().enumerate() {
+                    if let Some(ptr) = ptr_opt {
+                        slices.push(ptr);
+                    }
+                }
+
+                unsafe {
+                    T::iter(&slices[..], block.elements_count as usize, |x| f(x));
                 }
             }
         }
@@ -218,16 +295,26 @@
 
 #[cfg(test)]
 mod test {
-    use super::GearDataManager;
+    use super::{super::common::GearId, GearDataManager};
 
+    #[derive(Clone)]
     struct Datum {
         value: u32,
     }
 
     #[test]
-    fn iteration() {
+    fn single_component_iteration() {
+        assert!(std::mem::size_of::<Datum>() > 0);
+
         let mut manager = GearDataManager::new();
         manager.register::<Datum>();
-        manager.iter(|d: (&Datum,)| {});
+        for i in 1..=5 {
+            manager.add(GearId::new(i as u16).unwrap(), &Datum { value: i });
+        }
+
+        let mut sum = 0;
+        manager.iter(|(d,): (&Datum,)| sum += d.value);
+
+        assert_eq!(sum, 15);
     }
 }