Skip to content

Simple utilities library in C23 with no unnecessary overhead and implicit memory allocations.

License

Notifications You must be signed in to change notification settings

qsysctrl/qutil.c

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

75 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

qutil.c

Warning

Work on the library is in progress. In this state library interface is unstable and even code examples may be not working.

Simple utilities library in C23 with no unnecessary overhead and implicit memory allocations.

Unfortunately, for reason of no overhead goal, some functionality is difficult or even impossible for implement, and already implemented functionality seems quite clumsy.

Result and option types

Some features use compiler extensions. You may supress warnings by defining Q_PEDANTIC_SAFE before include qoption.h or qresult.h. If your compiler dont support these exntensions, that feature just not defining in header.

Result type

Q_DECLARE_RESULT(section_memory_t, common_error_t)

Q_RESULT(section_memory_t, common_error_t)
map_message_question_section(const struct question_section* q_sec,
                             struct memory* m) {
  ASSERT(q_sec != nullptr);
  ASSERT(memory_is_invalid(m) == false);

  // Necessary for context-dependent operations like Q_ERR, Q_OK 
  Q_RESULT_CONTEXT(section_memory_t, common_error_t);

  size_t dn_len = /* ... */;
  size_t req_size = /* ... */;
  if (!memory_is_available(m, req_size)) {
    // Returns Q_RESULT thats contains value of `common_error_t`
    return Q_ERR(E_NOT_ENOUGH_MEMORY); 
  }
  
  // ...
  
  // Returns Q_RESULT thats contains OK value 
  // of `section_memory_t` (aka struct section_memory)
  return Q_OK( (struct section_memory){
      .mem = (char*)m->ptr + start_offset,
      .count = 1,
      .nbytes = req_size,
  } );
}

Q_RESULT(memory_t, error_cont_t)
make_query(struct memory *mem, const char *name, enum QCLASS class,
           enum QTYPE type, enum OPCODE opcode, bool is_rd) {
  Q_RESULT_CONTEXT(memory_t, error_cont_t);

  // ...

  // Checks if `make_fqdn()` has been returned OK value,
  // otherwise returns ERR value from current function
  // Returned from current function Q_RESULT will be
  // contain ERR value of `make_fqdn`.
  // Obviously, the ERR types of both `make_fqdn` and 
  // current function should be equal.
  auto fqdn = Q_RES_TRY( make_fqdn(mem, name) );

  // ...
  
  auto q_sec = map_message_question_section(&q, mem);
  
  // Checks if `q_sec` contains ERR value
  if (!Q_RES_IS_OK(&q_sec)) {
    // If true, returns ERR value with error code from `q_sec` ERR value
    return Q_ERR( (struct error_cont){.error_code = Q_RES_GET_ERR(q_sec)} );
  }

  // ...

  // Q_RES_GET_OK_REF(&q_sec) returns pointer to `q_sec` OK value
  struct udp_message msg = Q_RES_TRY(
    map_udp_message(mem,
                     opcode,
                     is_rd,
                     Q_RES_GET_OK_REF(&q_sec),
                     nullptr,
                     nullptr,
                     nullptr)
  );

  // Returns OK value
  return Q_OK( (struct memory) { .ptr = msg.content, .nbytes = msg.content_nbytes, .offset = 0 } );
}

Option type

Q_DECLARE_OPTION(pointer_t) // Somewhere before using Q_OPTION(pointer_t)

Q_OPTION(pointer_t) parse_pointer(uint8_t data[static 2]) {
  Q_OPTION_CONTEXT(pointer_t); // Necessary for Q_NONE(), Q_VALUE(), Q_TRY() and so on

  uint16_t raw;
  memcpy(&raw, data, 2);
  raw = ntohs(raw);

  struct pointer ptr = {
      .F = (raw >> 14) & 0x3,
      .PTR = raw & 0x3FFF,
  };

  if (ptr.F != 0x3) {
    return Q_NONE(); // Returns `none`
  }
  return Q_VALUE( ptr ); // Returns value
}

int some_other_function() {
  // ...
  
  // Q_OPTION(pointer_t) name_ptr = parse_pointer((uint8_t*)from);
  // or:
  auto name_ptr = parse_pointer((uint8_t*)from);
  
  if (Q_OPT_HAS_VALUE(&name_ptr)) { // Checks if `name_ptr` contains value
    // Unwrapping:
    struct fqdn* name = (struct fqdn*)((char*)full_msg + Q_OPT_UNWRAP(name_ptr).PTR);
    // If unwrap fails, then program is aborted with 
    // message to stderr thats contains failed unwrap code location 
    
    // ...
  }
  // ...
}
Q_OPTION(message_header_t) // Declared above
form_message_header(enum OPCODE opcode, bool is_rd, uint16_t qdcount,
                           uint16_t ancount, uint16_t nscount,
                           uint16_t arcount) {
  Q_OPTION_CONTEXT(message_header_t);

  // Checks if `generate_message_id()` returns value
  // otherwise returns `none` from current `form_message_header` function
  // as if it do `return Q_NONE();` statement
  uint16_t id = Q_OPT_TRY(generate_message_id()); 

  // ...

  return Q_VALUE(result_hdr);
}

To do:

  • Documentation
  • result type
    • error propagation (TRY macros)
    • result_T_ERR_ok()
    • result_T_ERR_err()
    • result_T_ERR_get_err()
    • result_T_ERR_get_value()
    • result_T_ERR_unwrap()
    • result_T_ERR_unwrap_or()
    • result_T_ERR_inspect()
    • result_T_ERR_inspect_err()
    • result_T_ERR_and_then()
    • result_T_ERR_or_else()
    • result_T_ERR_map()
    • result_is_ok()
    • result_ok_err()
    • result_match()
  • option type
    • option propagation (TRY macros)
    • option_T_value()
    • option_T_none()
    • option_T_and_then()
    • option_T_or_else()
    • option_T_map()
    • option_T_unwrap()
    • option_T_unwrap_or()
    • option_T_take()
    • option_has_value()
    • option_match()

About

Simple utilities library in C23 with no unnecessary overhead and implicit memory allocations.

Resources

License

Stars

Watchers

Forks