Repository: FabricMC/fabric (Java)

Issue:
Transfer api bug: `nonEmptyIterator` doesn't handle views that become empty during iteration
This was first reported in MattiDragon/ExtendedDrawers#81 where the compacting drawer storage returned an empty view in `nonEmptyIterator` due to it becoming empty after the iterator checked it's state. The root cause is that the iterator implementation stores the next view, if available, on call to `next`. In cases such as the compacting drawer, a view can change before it has be returned from the iterator due to other views affecting it's state. I've implemented a fix in [this commit](https://github.com/MattiDragon/ExtendedDrawers/commit/ea8a83442ba381d8100a5e99292a9f4cec8eaa47), but it would be nice if the default implementation handled this edge case.



Fix (patch):
diff --git a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/storage/Storage.java b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/storage/Storage.java
index 6bab9d0c8d..f8a13706d0 100644
--- a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/storage/Storage.java
+++ b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/api/transfer/v1/storage/Storage.java
@@ -18,6 +18,8 @@
 
 import java.util.Iterator;
 
+import com.google.common.collect.Iterators;
+
 import net.fabricmc.fabric.api.transfer.v1.storage.base.CombinedStorage;
 import net.fabricmc.fabric.api.transfer.v1.storage.base.ExtractionOnlyStorage;
 import net.fabricmc.fabric.api.transfer.v1.storage.base.InsertionOnlyStorage;
@@ -145,7 +147,7 @@ default boolean supportsExtraction() {
 	 * @return An iterator over the non-empty views of this storage. Calling remove on the iterator is not allowed.
 	 */
 	default Iterator<StorageView<T>> nonEmptyIterator() {
-		return TransferApiImpl.filterEmptyViews(iterator());
+		return Iterators.filter(iterator(), view -> view.getAmount() > 0 && !view.isResourceBlank());
 	}
 
 	/**
diff --git a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/impl/transfer/TransferApiImpl.java b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/impl/transfer/TransferApiImpl.java
index 274915a556..cb8a1bd879 100644
--- a/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/impl/transfer/TransferApiImpl.java
+++ b/fabric-transfer-api-v1/src/main/java/net/fabricmc/fabric/impl/transfer/TransferApiImpl.java
@@ -94,44 +94,6 @@ public T next() {
 		};
 	}
 
-	public static <T> Iterator<StorageView<T>> filterEmptyViews(Iterator<StorageView<T>> iterator) {
-		return new Iterator<>() {
-			StorageView<T> next;
-
-			{
-				findNext();
-			}
-
-			private void findNext() {
-				while (iterator.hasNext()) {
-					next = iterator.next();
-
-					if (next.getAmount() > 0 && !next.isResourceBlank()) {
-						return;
-					}
-				}
-
-				next = null;
-			}
-
-			@Override
-			public boolean hasNext() {
-				return next != null;
-			}
-
-			@Override
-			public StorageView<T> next() {
-				if (!hasNext()) {
-					throw new NoSuchElementException();
-				}
-
-				StorageView<T> ret = next;
-				findNext();
-				return ret;
-			}
-		};
-	}
-
 	public static <T> List<SingleSlotStorage<T>> makeListView(SlottedStorage<T> storage) {
 		return new AbstractList<>() {
 			@Override