Adding Logic Flow Control to Your Programming

1. The Problem

In a computer program, besides the real execution flow control, logic flows are also very common. Sometimes, execution flow is the same as logic flow, but they could be different in many cases. For example, for a Web server to read a HTTP request from a socket, if you use synchronous socket, you will write something like this:

   void read(HTTP_REQUEST& http_request)
   {
      read(http_request.header);
      read(http_request.body, http_request.header);
   }

   void read(HTTP_REQUEST_HEADER& header)
   {
      string line = read_line();
      parse_request_link(line, header.method, header.uri,
                         header.version);

      while (TRUE)
      {
         line = read_line();
         if (line.empty())
            break;

         parse_header_field(line, header);
      }
   }

   void read(BYTE[]& body, HTTP_REQUEST_HEADER& header)
   {
      string transfer_encoding = header.fields['Transfer-Encoding'];
      if (transfer_encoding != b.chunkedb.)
         body = read_bytes(header.fields['Content-Length']);
      else
      {
         while (TRUE)
         {
            string chunk_header = read_line();
            DWORD chunk_size = atoi(chunk_header);
            if (chunk_size == 0)
               break;
            BYTE[] chunk_body = read_bytes(chunk_size);
            body += chunk_body;
         }
      }
   }

   string read_line()
   {
      while (TRUE)
      {
         int n = strpos(read_buffer, b.nb., read_buffer.size());
         if (n > 0)
            break;
         read_buffer += socket.read();
      }
      return read_buffer.extract(n);
   }

   Byte[] read_bytes(int sz)
   {
      while (TRUE)
      {
         if (sz <= read_buffer.size())
            break;
         read_buffer += socket.read();
      }
      return read_buffer.extract(sz);
   }

In this code, execution flow and logic flow are the same. However, if you use an asynchronous socket where you receive events passively, you have to write something like this:

when socket is available to read()
{
   read_buffer += socket.read();
   if (state == read_request_line)
   {
      if (!read_line(line))
         return;
      parse_request_link(line, method, uri, version);
      state = read_header_field;
   }
   while (state == read_request_line)
   {
      if (!read_line(line))
         return;
      if (line.empty())
      {
         transfer_encoding = header.fields['Transfer-Encoding'];
         if (transfer_encoding != b.chunkedb.)
         {
            content_length = header.fields['Content-Length'];
            state = read_body;
         }
         else
            state = read_chunk_header;
      }
      else
         parse_header_field(line, header, value);
   }
   if (state == read_body)
   {
      request_body += read_buffer;
      read_buffer.clear();
      if (request_body.size() >= content_length)
         state = read_finished;
      return;
   }
   if (state == read_chunk_header)
   {
      if (!read_line(line))
         return;
      chunk_size = atoi(line);
      if (chunk_size == 0)
      {
         state = read_finished;
         return;
      }
      state = read_body;
   }
   if (state == read_chunk_body)
   {
   // append at most chunk_size bytes
      request_body.append(read_buffer, chunk_size);
      if (chunk_size == 0)
         state = read_chunk_header;
      return;
   }
}

The execution flow is completely different whereas the logic flow remains the same. Because you can only receive data piece by piece, you have to remember state and some other variables so that you know what to do when an event comes. The code is usually longer. It’s harder to read/write/modify. And it’s easier to produce more bugs.

More by Author

Must Read