Skip to content

Commit

Permalink
Merge pull request #21 from MapIV/fix/invalid-file-loading
Browse files Browse the repository at this point in the history
Fix to prevent infinite loop when loading invalid files
  • Loading branch information
jacoblambert authored Jun 7, 2024
2 parents a7d9d65 + d9f60f4 commit 80ff123
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 6 deletions.
15 changes: 9 additions & 6 deletions src/pypcd4/pypcd4.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import string
import struct
from enum import Enum
from io import BufferedReader
from pathlib import Path
from typing import TYPE_CHECKING, BinaryIO, List, Literal, Optional, Sequence, Tuple, Union

Expand Down Expand Up @@ -171,7 +172,7 @@ def build_dtype(self) -> np.dtype[np.void]:
return np.dtype([x for x in zip(field_names, np_types)])


def _parse_pc_data(fp: BinaryIO, metadata: MetaData) -> npt.NDArray:
def _parse_pc_data(fp: BufferedReader, metadata: MetaData) -> npt.NDArray:
dtype = metadata.build_dtype()

if metadata.points > 0:
Expand Down Expand Up @@ -217,13 +218,15 @@ def __init__(self, metadata: MetaData, pc_data: npt.NDArray) -> None:
self.pc_data = pc_data

@staticmethod
def from_fileobj(fp: BinaryIO) -> PointCloud:
def from_fileobj(fp: BufferedReader) -> PointCloud:
lines: List[str] = []
while True:
line = fp.readline().strip()
lines.append(line.decode(encoding="utf-8") if isinstance(line, bytes) else line)
for bline in fp:
if (line := bline.decode(encoding="utf-8").strip()).startswith("#") or not line:
continue

lines.append(line)

if lines[-1].startswith("DATA"):
if line.startswith("DATA") or len(lines) >= 10:
break

metadata = MetaData.parse_header(lines)
Expand Down
8 changes: 8 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,3 +315,11 @@ def xyzrgb_binary_compressed_path():
@pytest.fixture
def xyzintensity_binary_compressed_organized_path():
return f"{Path(__file__).resolve().parent}/pcd/binary_compressed_organized.pcd"

@pytest.fixture
def ascii_empty_path():
return f"{Path(__file__).resolve().parent}/pcd/ascii_empty.pcd"

@pytest.fixture
def ascii_invalid_header_path():
return f"{Path(__file__).resolve().parent}/pcd/ascii_invalid_header.pcd"
Empty file added tests/pcd/ascii_empty.pcd
Empty file.
3 changes: 3 additions & 0 deletions tests/pcd/ascii_invalid_header.pcd
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# .PCD v.7 - Point Cloud Data file format
VERSION .7
FIEL
10 changes: 10 additions & 0 deletions tests/test/test_pypcd4.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,16 @@ def test_load_binary_compressed_organized_pcd(xyzintensity_binary_compressed_org
assert len(pc.pc_data) == pc.metadata.points


def test_load_ascii_empty_pcd(ascii_empty_path):
with pytest.raises(ValidationError):
PointCloud.from_path(ascii_empty_path)


def test_load_ascii_invalid_header_pcd(ascii_invalid_header_path):
with pytest.raises(ValidationError):
PointCloud.from_path(ascii_invalid_header_path)


def test_from_points():
array = np.array([[1, 2, 3], [4, 5, 6]])
fields = ("x", "y", "z")
Expand Down

5 comments on commit 80ff123

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage

Coverage Report
FileStmtsMissCoverMissing
src/pypcd4
   __init__.py20100% 
   _version.py11282%5–6
   pointcloud2.py591280%87–101
   pypcd4.py3913292%24–25, 136, 189, 352–353, 575–597, 610–651, 661–662, 881, 901–902, 1025, 1028
TOTAL4634690% 

Tests Skipped Failures Errors Time
65 0 💤 0 ❌ 0 🔥 0.897s ⏱️

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage

Coverage Report
FileStmtsMissCoverMissing
src/pypcd4
   __init__.py20100% 
   _version.py11282%5–6
   pointcloud2.py591280%87–101
   pypcd4.py3913292%24–25, 136, 189, 352–353, 575–597, 610–651, 661–662, 881, 901–902, 1025, 1028
TOTAL4634690% 

Tests Skipped Failures Errors Time
65 0 💤 0 ❌ 0 🔥 0.929s ⏱️

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage

Coverage Report
FileStmtsMissCoverMissing
src/pypcd4
   __init__.py20100% 
   _version.py11282%5–6
   pointcloud2.py591280%87–101
   pypcd4.py3913292%24–25, 136, 189, 352–353, 575–597, 610–651, 661–662, 881, 901–902, 1025, 1028
TOTAL4634690% 

Tests Skipped Failures Errors Time
65 0 💤 0 ❌ 0 🔥 0.874s ⏱️

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage

Coverage Report
FileStmtsMissCoverMissing
src/pypcd4
   __init__.py20100% 
   _version.py11282%5–6
   pointcloud2.py591280%87–101
   pypcd4.py3913292%24–25, 136, 189, 352–353, 575–597, 610–651, 661–662, 881, 901–902, 1025, 1028
TOTAL4634690% 

Tests Skipped Failures Errors Time
65 0 💤 0 ❌ 0 🔥 0.891s ⏱️

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage

Coverage Report
FileStmtsMissCoverMissing
src/pypcd4
   __init__.py20100% 
   _version.py11282%5–6
   pointcloud2.py591280%87–101
   pypcd4.py3913292%24–25, 136, 189, 352–353, 575–597, 610–651, 661–662, 881, 901–902, 1025, 1028
TOTAL4634690% 

Tests Skipped Failures Errors Time
65 0 💤 0 ❌ 0 🔥 0.903s ⏱️

Please sign in to comment.