Skip to content

Code Challenge #55: Creating an xxd "clone" using Swift with XCode

License

Notifications You must be signed in to change notification settings

meaxsom/55-xxd-swift

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

29 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Coding Challenge 55: xxd

Background

Challenge #55 is for an xxd clone.

Stretch Challenge

Complete the challenge using Swift as a command line app for macOS.

  • use XCode tooling and standard Swift based libraries as much as possible, e.g. logging, testing frameworks etc.

  • I've used Swift on projects before for building Apps but it's been a while. Hopefully this will refresh the Swift basics for me.

  • Things that I see as challenges in this effort:

    • Stucts vs Classes
    • Swift I/O
    • Command Line argument processsing
    • Getting lost in how much Swift has evolved since I last used it
  • Although we're using Swift this should be runnable under Linux with the proper compilier/libraries

Toolchain

  • XCode 15.3

Step 0

  • Set up development enviornment

    • Create XCode command line progject
    • init git repo locally and on Github
  • Run "hello world"

  • run command line tool from terminal

    • Product -> Show Build Folder in Finder
  • figure out command line processing in Swift

    • The Swift ArgumentParser package looks like the way to go
    • Add by File -> Add Package Dependencies...
      • Generates a Package.swift file with the approperiate entries somewhere in the project presumabally
    • Turns out that using ArgumentParser on a struct with other non-argument properties causes all kinds of hell with Decoder. Not something I enjoyed tracking down right out of the gate
  • figure out testing framework in Swift

    • XCTest is the built in one for XCode but there are other frameworks out there
    • This was WAY HARDER than I was expecting. I could not figure out how to get it to include the struct I wanted to test. Turns out I had to add the file with the struct under test to the test "target".
  • figure out logging in Swift

    • Logger struct is evidenlty the way to go
    • This was where I ran into problems with the commnad line processing
  • Rewrite "hello world" to use commandline, logging and testing

    • @main explained
    • as I mentioned earlier, getting through this took way longer. Really fighting documentation issues. Phind helped but still lots of struggles. At least I'm ready to move on to step 1...

Step 1

Basically dump some input file out as "hex" like xxd does

  • need a command line argument to read the file
  • format the output in 3 sections:
    • offset from beginning of the file, in hex - 8 bytes long
    • the hex output - 16 bytes long in a default of 4 "octets"
    • the ascii output - also 16 bytes but in char form. anything outside of printable chars is a .

Notes

  • Opted to read the file in "chunks" of 16 x UInt8 using an extension to FileManager

  • For each chunk, generate a String representation from the printable characters - as an extention to the String type

  • Also for each chunk - divide into an array of arrays represeting the desired octet output "grouping". This is what we'll need in Step 2, but got that out of the way in this step. Again, used an extension this time with the Array type

  • For those grouped items, generate a hex representation for outut. Leveraged an extension in Array type

  • Keep track of the file offset and output that as hex

  • learned a lot about using map & reduce functions in iterating over the array of bytes and array of arrays!

  • Feels like there is too much struct creation going on for moving bytes around.

    • need some way to observe memory pressure/use
  • also got to be a better way to conver a byte into a char for concat into a string. I do need to do that one byte at a time for printable character reasons, but that's a lot of iterating

  • Basically feeling like it works but is sub optimal - although the examples I've looked at basically do what the codes doing. Who knows

Step 2

This step is about supporting the -e (little-endian) flag and -g (group) option

  • ArgumentParser library should come in handy here. Probably use some of the more specific syntax
  • Grouping should already be done through the "chunking" call
  • I'll need to come up with a strategy for converting to the [UInt8] chunks by swapping around bytes based on the chunk size
    • will also need to validate that the "group" is divisible by 2 if the endian option is used

Notes

  • added flags and options to the command line to handle the input
  • the grouping was pretty easy since I already had the chunking figured out
  • ended up writing a toLittleEndian() extension for [UInt8] and added a if statement to handle the flag

Step 3

support the command line options to set the number of octets to be written out and the number of columns to print per line. These are the -l and -c flags [if] you want to explore the valid settings in the man entry.

  • the "len" option is just how many total octets to read from the file

    • Added OctetCounter to help with octet bookkeeping
    • Adding padding to the hex output so incomplete lines will line up
  • the "col" option is how many octets to read at once.

    • up to this point, 16's been the default column size so that needs to become flexible
    • OctetCounter now controls the size of the read, which is the number of "cols"

Notes

  • again, given the existing structure, this wasn't all that much of a change. Mainly rerouting some constants already in use.
  • consolidating the logic in the OctetCounter make it pretty easy
  • not sure I have the logic right between -g, -e and -c but the standard xxd seems to ignore -g when -c is in use

Step 4

support the -s flag which allows us to seek to a specific byte offset in the file

Notes

  • This was easy as well. Just added the @Option and hooked it into the logic I already anticipated for seeking to a specific offest in the file to begin reading
  • It also appears that the swift-argument-parser got wonky in the build process. Had to readd it.

Step 5

support the ability to revert a hex dump back to a binary file. This is the -r option.

So:

  • Read in the file line at a time
  • for earch line:
    • parse out what's between the line count and the ASCII field
    • remove/trim any space - should be a hex string at this point
    • convert the hex string to a series of bytes in an array
      • this as part of the testing infrastructure!!
    • write that out to standard out as binary(??)

Might be easier than I though.. so regex (ya I know I have 2 problems now) and moving a testing extension into the main code.

I may have to break off the main code into 2 function.. one for regular processing and one for reverse processsing

Notes

  • Mainly correct except I stumbled a bit figuring out how to write out a sequence of bytes. print doesn't work well w/binary data so I had to use write to stdout
  • Not impressed with the RegEx "builder" stuff. Way too wordy for my taste. I guess if you don't know regex it might be more helpful but w/30 years of regex behind me I wasn't going to do the builder pattern for what I can put in a string. This converter was kind of interesting.
  • Swift has a way to go with handling file I/O with simple things like reading a line of data. Should not be that hard.
  • Also not impressed with all the byte manipulation to/from UInt8s to various things. Swift might allow access to low level Bytes, but it doesn't make it easy.

Summary

  • Interesting project. I didn't know a lot about xxd but it's an interting utility.
  • Enjoyed writing a lot of what I needed as Swift extension.
  • Testing framework could be better
  • Command line processing could be better
  • File I/O could be better
  • The amount of abstraction/structure is staggering in Swift. Python was a lot easier as was Objective-C.
  • Getting compitant at Swift will take some work.
  • I'm sure a lot of this could be optimized but for now I'm calling #55 done!

About

Code Challenge #55: Creating an xxd "clone" using Swift with XCode

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages