1. How will code style be graded?

    Here are some guidelines:

    • Complex pieces of code should be documented in comments so that they become easier for the reader to understand—but don't overdo it with comments for trivial things.
    • Common, general-purpose functionality should be decomposed into helper functions to avoid repeated code—don't overdo this either, e.g. by making it difficult to understand the flow of execution.
    • Code should use consistent naming conventions and not contain commented-out code or vestiges of debugging.
    • Please check the return values of all functions in which an error could occur, and handle these errors gracefully. Throwing an exception is one graceful way to handle an unrecoverable error.
    • Avoid memory errors and leaks, egregious performance issues (e.g. allocating large amounts of memory for no reason) or unnecessarily complex approaches (e.g. a complicated data structure that is only marginally more efficient than something much simpler, or something available in the C++ standard library).
    • You can check your code for memory leaks, invalid accesses, and undefined behavior by compiling with cmake .. -DCMAKE_BUILD_TYPE=RelASan and then make clean, make, then make check or running any of the individual apps.
    • Before submitting each assignment, please run make format (to standardize the formatting), and make sure to remove any dummy code or comments that were included with the starter code.
    Your style grade may also include components of functionality not covered by the tests.

  2. How much of my grade will come from style and to the quality of my writeup?
  3. Grading breakdown for labs: 15% style, 10% writeup, 75% functionality.

  4. How do I run an individual test?
  5. ctest -R '^test_name$'

  6. Can I add include lines in the header files?
  7. Sure.

  8. Why does 'make check' fail even though it's in the Sponge README?
  9. Short Answer: This is a typo in our documentation. You should run make check_lab0, not make check.
    Long Answer: The make target, check, runs the entire sponge test suite. The full test suite requires a shell script,, which you won't get until a later lab. The make target check_lab0 will just test lab0. You should be able to run it with the lab0 starter code (although it will return an error until you've completed the lab!).

  10. What's the procedure if I want to use some of my late days?
  11. To use a late day, please email the staff list ( by the lab deadline, letting us know if you are using late days. With weekly labs, the CAs want to be able to get feedback to you quickly. As such, we will start grading as soon as the deadline passes, and want to avoid redundantly grading submissions that weren't intended to be final.

  12. How do I use git?
  13. Here are some links to resources to learn Git.

    Brief summary: Git commits store a snapshot of the current state of your code. For instance, say you break a feature that you know used to work, but aren't sure which code caused it to break. If you made a commit after getting that feature working, you can checkout that commit to see what things looked like in the good old days when it worked. So, make a commit every time you get something working! To make a commit:
    First, git add the files you've changed - you must add before every commit. e.g. git add ../libsponge/byte_stream.*
    Then, commit with a message describing the state of the code. e.g. git commit -m "All tests pass except many_writes times out."
    You can see which files you modified by running git status. To really understand the state of a Git repository, the command-line tig tool, or the graphical gitk tool, can be very helpful.
    A handy shortcut to commit all modified files is git commit -am "[message]".
    Don't git push - when you're ready to submit, you'll use git bundle instead to package your commits.

    Sanity check: If you want to verify that you have submitted exactly the files you intended to, you may find the following useful.
    First, once you've committed, run git status. You should see a message saying nothing to commit, working tree clean. If you have uncommitted changes, you'll see this instead: Changes not staged for commit:.
    Second, after you've copied your git bundle to rice, and logged into rice:

    1. Run $ git clone [bundle name] [unbundled name].
    2. cd into the unbundled directory.
    3. Run $ git log and verify that you see the commits you expect to.
    4. Copy the commit hash of the starter code commit.
    5. Run $ git diff [hash] to make sure they see all changes you want to submit.

  14. How do I debug?
  15. gdb is a great tool for debugging the labs! Check out the CS107 guide if you need a refresher.
    To use gdb to debug a test:

    1. Install gdb (on the VM, sudo apt-get install gdb).
    2. Start gdb on the executable corresponding to the test you want to debug (from the build directory, ls tests to see the executables). e.g. from the build directory: gdb tests/byte_stream_one_write.
    3. The output of the test failure will show you the part of the test that failed. To set a breakpoint at that part of the test, break on the line in the test file where the test harness for that part is created. The test files are in sponge/tests (not sponge/build/tests). e.g. if you're failing the write-pop2-end test in byte_stream_one_write, break 83 (i.e. where the write-pop2-end test harness is created).
    4. You can set breakpoints on your functions using the function names, as usual.
    Other notes:

Lab 0

  1. What should the behaviour of my program be if the caller tries to peek or pop with a len greater than what is available?
  2. We don't have any preference on the behavior if the caller tries to peek or pop more than is available in the stream, as long as you behave reasonably and don't crash in that situation. If you want to peek/pop the maximum available, that is fine with us. If you want to throw an exception (either for an overlength peek or an overlength pop), that is also fine with us.

Lab 1

  1. Which bytes should be accepted in a push_substring call?
  2. Those whose index is within capacity indices of the first unread byte in the re-assembled stream. Pictorally:
  3. Is it okay for our re-assembly data structure to store overlapping substrings?
  4. It's possible to implement an interface-correct reassembler which stores overlapping substrings. However, allowing the re-assembler to do this undermines the notion of capacity as a memory limit, so we'll consider the storage of overlapping substrings to be a style violation when grading.

Lab 2

  1. Can we use static_cast in functions for WrappingInt32?
  2. Yes, these kinds of functions (those that explicitly convert between numeric types) present exactly the kind of situation in which static_cast is appropriate. Note also that you don't need to use static_cast if you don't want to—you can explicitly construct a WrappingInt32 with syntax like WrappingInt32{x}.

  3. Should TCPReceiver::segment_received() return true if any part of the segment is inside the window, even if both the beginning and end of the segment are outside the window?
  4. Good question—please see our Piazza note where we clarify this. The short answer is that we've deleted a test in the lab2 starter code in order to make both answers to this question (yes and no) acceptable.

Lab 4

  1. In TCPConnection::segment_received, what are the three conditions in which the TCPConnection needs to make sure that the segment receives at least one ACK segment in reply, and may need to force the TCPSender to spit out an empty segment to make this happen?
    1. If the incoming segment occupies any sequence numbers (length_in_sequence_space() > 0)
    2. If the TCPReceiver thinks the segment is unacceptable (TCPReceiver::segment_received() returns false)
    3. If the TCPSender thinks the ackno is invalid (TCPSender::ack_received() returns false)

  2. Okay but hang on, what happens if the TCPConnection wants to send an ACK segment but the TCPReceiver's ackno is missing (e.g. because it hasn't gotten an incoming SYN yet)?
    If the ackno is missing, then you can't send an ACK segment. Don't send one in this case.

  3. When is the connection fully over (i.e., when should active() start returning false?

    There are two ways a connection can fully end:

    • [Unclean shutdown] Any segment with the RST flag is sent or received, or
    • [Clean shutdown] The sender is totally done (meaning, the sender's input stream is at EOF with no bytes in flight) AND the receiver is totally done (meaning, the receiver's output stream has ended) AND either
      • _linger_after_streams_finish is false (no need to linger), or
      • time_since_last_segment_received() >= 10 * _cfg.rt_timeout (we've lingered long enough)

  4. I think I may be having some trouble with my logic about when a connection is fully over (i.e., when to stop returning true from active()). Some of my tests are timing out. How can I debug this?

    Check out this Piazza thread for a debugging approach that may help. It may be easier to run the client and server manually (as described there) if you want to figure out where things are going awry.

  5. The test suite keeps talking about the state of the TCPReceiver and TCPSender—what are these states?
    The test suite (and the tcp_state.hh and files) uses the public interface of your TCPSender and TCPReceiver to put them in broad categories or states. The tests make sure that your TCPSender/TCPReceiver are in the right state at the right time, given the actions taken.

    Here is a diagram of the expected evolution (and definition of each state) for the TCPReceiver. The receiver starts with no ISN (and therefore no ackno) (LISTEN). Then it moves to a state SYN_SENT where it has received a SYN segment (so there is an ackno) but hasn't assembled a FIN segment (so the stream hasn't ended). This is where most of the connection happens. Eventually the receiver assembles a FIN segment and ends the stream (FIN_RECV).

    And here is a diagram of the expected evolution (and definition of each state) for the TCPSender. The sender starts with nothing sent (CLOSED), then moves to a state SYN_SENT where it has sent something but nothing has been acknowledged. At some point, some bytes are acknowledged (SYN_ACKED). This is where most of the connection happens. Eventually, the stream ends and the sender sends a FIN segment (FIN_SENT), and some time after that, the stream is over and everything has been acknowledged (FIN_ACKED).