{"id":25613550,"url":"https://github.com/sajjon/bytepattern","last_synced_at":"2026-05-12T02:30:21.763Z","repository":{"id":63920442,"uuid":"535003838","full_name":"Sajjon/BytePattern","owner":"Sajjon","description":"A pattern finder for bytes","archived":false,"fork":false,"pushed_at":"2022-09-17T18:16:28.000Z","size":59,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-02-14T14:41:16.695Z","etag":null,"topics":["big-endian","byte-arrays","bytes","compare","endianess","hexadecimal","little-endian","pattern-recognition","swift","xctassertequal"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Sajjon.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-09-10T13:18:47.000Z","updated_at":"2024-03-13T13:45:04.000Z","dependencies_parsed_at":"2023-01-14T14:00:51.457Z","dependency_job_id":null,"html_url":"https://github.com/Sajjon/BytePattern","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sajjon%2FBytePattern","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sajjon%2FBytePattern/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sajjon%2FBytePattern/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sajjon%2FBytePattern/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Sajjon","download_url":"https://codeload.github.com/Sajjon/BytePattern/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240113945,"owners_count":19749829,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["big-endian","byte-arrays","bytes","compare","endianess","hexadecimal","little-endian","pattern-recognition","swift","xctassertequal"],"created_at":"2025-02-22T01:36:05.475Z","updated_at":"2026-05-12T02:30:21.654Z","avatar_url":"https://github.com/Sajjon.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# BytePattern\n\n\nA **linear time** byte pattern finder, useful to discover that two bytes seqences are almost identical, probably they are but during construction of one of them the developer has accidently either reversed the sequence, or they originate from integers, which might accidently use the wrong endianess, or a combination of both.\n\n**All examples below assume that the bytes sequences `LHS` and `RHS`\nhave the same length.**\n\n## `identical`\nTwo byte sequences are identical, i.e. equal to each other.\n\n```swift\nlet finder = BytePatternFinder()\n\nfinder.find(\n    lhs: try Data(hex: \"dead beef 1234 5678 abba 0912 deed fade\"),\n    rhs: try Data(hex: \"dead beef 1234 5678 abba 0912 deed fade\")\n) // `.identical`\n```\n\n## `reversed`\nTwo byte sequences would be identical if one of them where reversed in its entirety.\n\n```swift\nfinder.find(\n    lhs: \"ab12 cd34\",\n    rhs: \"34cd 12ab\"\n) // `.sameIf([.reversed])`\n\nfinder.find(\n    lhs: \"dead beef 1234 5678 abba 0912 deed fade\",\n    rhs: \"defa edde 1209 baab 7856 3412 efbe adde\"\n) // `.sameIf([.reversed])`\n```\n\n## `reversedHex`\n Two byte sequences would be identical if one of them where initialized from a hex string that was revered before passed to byte sequence init.\n \n Or in otherwords, if every byte in one of the sequences were [rotated (ciruclar shifted)](https://en.wikipedia.org/wiki/Bitwise_operation#Rotate) by 4 bits. This operator is not part of the standard library but is implemented by: `(input \u003e\u003e 4) | (input \u003c\u003c 4)`.\n\n```swift\nfinder.find(\n    lhs: \"ab12 cd34\",\n    rhs: \"43dc 21ba\"\n) // `.sameIf([.reversedHex])`\n\nfinder.find(\n    lhs: \"dead beef 1234 5678 abba 0912 deed fade\",\n    rhs: \"edaf deed 2190 abba 8765 4321 feeb daed\"\n) // `.sameIf([.reversedHex])`\n```\n\n## `reverseOrderOfUInt16/32/64sFromBytes`\n\nTwo byte sequences would be identical if one of them were \"chunked\" into segments of length 2/4/8, i.e. as if loaded `UInt16`/`UInt32`/`UInt64` at start of each segment, and then this list of integers were reversed, for `reverseOrderOfUInt16sFromBytes`/`reverseOrderOfUInt32sFromBytes`/`reverseOrderOfUInt64sFromBytes`.\n\n```swift\nfinder.find(\n    lhs: \"dead beef 1234 5678\",\n    rhs: \"5678 1234 beef dead\"\n) // `.sameIf([.reverseOrderOfUInt16sFromBytes])`\n\nfinder.find(\n    lhs: \"dead beef 1234\",\n    rhs: \"1234 beef dead\"\n) // `.sameIf([.reverseOrderOfUInt16sFromBytes])`\n```\n\n## `swapEndianessOfUInt16/32/64sFromBytes`\n\n Two byte sequences would be identical if one of them were \"chunked\" into segments of length 2/4/8, i.e. as if loaded `UInt16`/`UInt32`/`UInt64` and we were to swap endianness of each integer in this list, for `swapEndianessOfUInt16sFromBytes`/`swapEndianessOfUInt32sFromBytes`/`swapEndianessOfUInt64sFromBytes`.\n\n```swift\nfinder.find(\n    lhs: \"ab12 cd34\",\n    rhs: \"12ab 34cd\"\n) // `.sameIf([.swapEndianessOfUInt16sFromBytes])`\n\n\nfinder.find(\n    lhs: \"dead beef 1234 5678 abba 0912 deed fade\",\n    rhs: \"adde efbe 3412 7856 baab 1209 edde defa\"\n) // `.sameIf([.swapEndianessOfUInt16sFromBytes])`\n\n\nfinder.find(\n    lhs: \"deadbeef 12345678 abba0912 deedfade\",\n    rhs: \"efbeadde 78563412 1209baab defaedde\"\n) // `.sameIf([.swapEndianessOfUInt32sFromBytes])`\n\nfinder.find(\n    lhs: \"deadbeef12345678 abba0912deedfade\",\n    rhs: \"78563412efbeadde defaedde1209baab\"\n) // `.sameIf([.swapEndianessOfUInt64sFromBytes])`\n```\n\n## Combined\n\n```swift\nfinder.find(\n    lhs: \"dead beef 1234\",\n    rhs: \"edda ebfe 2143\",\n) // `sameIf([.swapEndianessOfUInt16sFromBytes, .reverseOrderOfUInt16sFromBytes, .reversedHex])`\n```\n\n# XCTAssertBytesEqual\nA small test util package which enables you to conveniently compare byte sequences using the `BytePatternFinder`. It contains some `XCTAssert` like methods, but specially tailored for byte sequence comparision.\n\n\n## XCTAssertBytesEqual\n\n\n```swift\n// This test will fail.\n// Assertion failure message is:\n// \"Expected bytes in LHS to equal RHS, but they are not, however, they resemble each other with according to byte pattern: sameIf([swapEndianessOfUInt16sFromBytes]).\"\nfunc test_data_failing() throws {\n    try XCTAssertBytesEqual(\n        Data(hex: \"ab12 cd34\"),\n        Data(hex: \"12ab 34cd\"),\n        \"An optional message goes here.\"\n    )\n}\n```\n\nYou can change the behaviour of the test to pass for non-`identical` patterns found - but will still fail for no pattern found of course, by passing `passOnPatternNonIdentical: true`.\n\n```swift\nfunc test_data_passing() throws {\n    try XCTAssertBytesEqual(\n        Data(hex: \"ab12 cd34\"),\n        Data(hex: \"12ab 34cd\"),\n        \"An optional message goes here.\",\n        passOnPatternNonIdentical: true\n    )\n}\n```\n\nYou can also opt in to interrupt on non-identical patterns found by passing `haltOnPatternNonIdentical: true`:\n\n```swift\nfunc test_data_passing_halting() throws {\n    try XCTAssertBytesEqual(\n        Data(hex: \"ab12 cd34\"),\n        Data(hex: \"12ab 34cd\"),\n        \"An optional message goes here.\",\n        passOnPatternNonIdentical: true,\n        haltOnPatternNonIdentical: true\n    )\n}\n```\n\nYou can also globally change default value of `passOnPatternNonIdentical` and `haltOnPatternNonIdentical` by setting these properties on global type `DefaultXCTAssertBytesEqualParameters`. A good place to do this is in the `setUp()` method of your test class.\n\n\n```swift\noverride func setUp() {\n    super.setUp()\n    DefaultXCTAssertBytesEqualParameters.passOnPatternNonIdentical = true\n    DefaultXCTAssertBytesEqualParameters.haltOnPatternNonIdentical = true\n}\n\nfunc test_data_passing_halting_defaulParamsUsed() throws {\n    try XCTAssertBytesEqual(\n        Data(hex: \"ab12 cd34\"),\n        Data(hex: \"12ab 34cd\")\n    )\n}\n```\n\n\n## XCTAssertBytesFromHexEqual\n\nThe examples above can be simplified by used of `XCTAssertBytesFromHexEqual`, which will fail with error if you're passing in invalid hexadecimal strings.\n\n\n```swift\nfunc test_nonIdentical_but_passing() {\n    XCTAssertBytesFromHexEqual(\n        \"ab12 cd34\",\n        \"12ab 34cd\",\n        passOnPatternNonIdentical: true\n    )\n}\n```\n\n# BytesMutation\n\nThis small package allows you to perform mutation on any byte sequence conforming to `ContiguousBytes` and which names mirror those of `BytePattern`.\n\n```swift\npublic extension ContiguousBytes {\n    func reversed() -\u003e [UInt8]\n\n    func reversedHex() -\u003e [UInt8]\n\n    func reverseOrderOfUInt16sFromBytes() -\u003e [UInt8]\n\n    func reverseOrderOfUInt32sFromBytes() -\u003e [UInt8]\n\n    func reverseOrderOfUInt64sFromBytes() -\u003e [UInt8]\n\n    func swapEndianessOfUInt16sFromBytes() -\u003e [UInt8]\n\n    func swapEndianessOfUInt32sFromBytes() -\u003e [UInt8]\n\n    func swapEndianessOfUInt64sFromBytes() -\u003e [UInt8]\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsajjon%2Fbytepattern","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsajjon%2Fbytepattern","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsajjon%2Fbytepattern/lists"}