/ca/ex3/phone_book.c

https://github.com/yasuhito/twodaycourses · C · 350 lines · 263 code · 57 blank · 30 comment · 18 complexity · 11724ff0db1dd043311355923c4b85e7 MD5 · raw file

  1. #include "phone_book.h"
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #define MAX_PHONE_NAME_LEN 32
  5. struct phone_book_name
  6. {
  7. char letters[MAX_PHONE_NAME_LEN + 1];
  8. };
  9. typedef struct phone_book_name phone_book_name;
  10. static phone_book_name make_phone_book_name(const char * letters)
  11. {
  12. phone_book_name name;
  13. strncpy(name.letters, letters, MAX_PHONE_NAME_LEN);
  14. name.letters[MAX_PHONE_NAME_LEN] = '\0';
  15. return name;
  16. }
  17. static int phone_book_name_compare(const phone_book_name * lhs,
  18. const phone_book_name * rhs)
  19. {
  20. return strcmp(lhs->letters, rhs->letters);
  21. }
  22. //--------------------------------------------------
  23. #define MAX_PHONE_NUMBER_LEN 32
  24. struct phone_book_number
  25. {
  26. char digits[MAX_PHONE_NUMBER_LEN + 1];
  27. };
  28. typedef struct phone_book_number phone_book_number;
  29. struct phone_book_entry
  30. {
  31. phone_book_name name;
  32. phone_book_number number;
  33. };
  34. typedef struct phone_book_entry phone_book_entry;
  35. static phone_book_number make_phone_book_number(const char * digits)
  36. {
  37. phone_book_number number;
  38. strncpy(number.digits, digits, MAX_PHONE_NUMBER_LEN);
  39. number.digits[MAX_PHONE_NUMBER_LEN] = '\0';
  40. return number;
  41. }
  42. static size_t min_size_t(size_t lhs, size_t rhs)
  43. {
  44. return lhs < rhs ? lhs : rhs;
  45. }
  46. static int phone_book_number_compare(const phone_book_number * lhs,
  47. const phone_book_number * rhs)
  48. {
  49. // by restricting the string comparison to the length of
  50. // the shorter string we make bsearch find
  51. // numbers that are prefixes of other numbers.
  52. // This does not compromise the phone book as long
  53. // as we never allow it to contain numbers that are
  54. // prefixes of other numbers.
  55. // Example: 123 is a prefix of 1234 and so if 1234
  56. // is a number already in the phone book we must not
  57. // be allowed to add the number 123 to it. Similarly
  58. // if 123 is already in the phone book we must not be
  59. // allowed to add the number 1234 since that too would
  60. // create a number that was a prefix of another number;
  61. // a number that was "unreachable"
  62. const size_t min_length = min_size_t(strlen(lhs->digits), strlen(rhs->digits));
  63. return strncmp(lhs->digits, rhs->digits, min_length);
  64. }
  65. //--------------------------------------------------
  66. struct phone_book
  67. {
  68. size_t max_size;
  69. phone_book_entry * entries;
  70. phone_book_entry ** name_index;
  71. phone_book_entry ** number_index;
  72. size_t index_size;
  73. };
  74. phone_book * phone_book_create(size_t max_size)
  75. {
  76. if (max_size == 0)
  77. return NULL;
  78. phone_book * created = malloc(sizeof *created);
  79. *created =
  80. (phone_book)
  81. {
  82. .max_size = max_size,
  83. .entries = calloc(max_size, sizeof(phone_book_entry)),
  84. .name_index = malloc(max_size * sizeof(phone_book_entry *)),
  85. .number_index = malloc(max_size * sizeof(phone_book_entry *)),
  86. .index_size = 0,
  87. };
  88. return created;
  89. }
  90. //--------------------------------------------------
  91. void phone_book_destroy(phone_book * destroy)
  92. {
  93. free(destroy->number_index);
  94. free(destroy->name_index);
  95. free(destroy->entries);
  96. free(destroy);
  97. }
  98. //--------------------------------------------------
  99. static phone_book_entry make_phone_entry(const char * name, const char * number)
  100. {
  101. return (phone_book_entry)
  102. {
  103. .name = make_phone_book_name(name),
  104. .number = make_phone_book_number(number),
  105. };
  106. }
  107. //--------------------------------------------------
  108. static int phone_book_indexed_compare_name(const void * lhs, const void * rhs)
  109. {
  110. const phone_book_entry * const * const lhs_entry = lhs;
  111. const phone_book_entry * const * const rhs_entry = rhs;
  112. return phone_book_name_compare(&(*lhs_entry)->name, &(*rhs_entry)->name);
  113. }
  114. static void phone_book_name_sort(phone_book * pb)
  115. {
  116. qsort
  117. (
  118. pb->name_index,
  119. pb->index_size,
  120. sizeof(phone_book_entry *),
  121. phone_book_indexed_compare_name
  122. );
  123. }
  124. static
  125. phone_book_entry * *
  126. phone_book_find_entry_by_name(const phone_book * pb,
  127. const char * name)
  128. {
  129. phone_book_entry * index =
  130. &(phone_book_entry)
  131. {
  132. .name = make_phone_book_name(name),
  133. .number.digits[0] = '\0',
  134. };
  135. phone_book_name_sort((phone_book *)pb);
  136. return bsearch
  137. (
  138. &index,
  139. pb->name_index,
  140. pb->index_size,
  141. sizeof(phone_book_entry *),
  142. phone_book_indexed_compare_name
  143. );
  144. }
  145. const char * phone_book_find_number(const phone_book * pb, const char * name)
  146. {
  147. phone_book_entry * * found =
  148. phone_book_find_entry_by_name(pb, name);
  149. return found ? (*found)->number.digits : NULL;
  150. }
  151. void phone_book_print_by_name(const phone_book * pb,
  152. void print(const char * name, const char * number))
  153. {
  154. phone_book_name_sort((phone_book *)pb);
  155. for (size_t at = 0; at != pb->index_size; at++)
  156. {
  157. const phone_book_entry * entry = pb->name_index[at];
  158. print(entry->name.letters, entry->number.digits);
  159. }
  160. }
  161. //-----------------------------------------------
  162. static int phone_book_indexed_compare_number(
  163. const void * lhs_entry, const void * rhs_entry)
  164. {
  165. const phone_book_entry * const * const lhs = lhs_entry;
  166. const phone_book_entry * const * const rhs = rhs_entry;
  167. return phone_book_number_compare(&(*lhs)->number, &(*rhs)->number);
  168. }
  169. static void phone_book_number_sort(phone_book * pb)
  170. {
  171. qsort
  172. (
  173. pb->number_index,
  174. pb->index_size,
  175. sizeof(phone_book_entry *),
  176. phone_book_indexed_compare_number
  177. );
  178. }
  179. static
  180. phone_book_entry * *
  181. phone_book_find_entry_by_number(const phone_book * pb,
  182. const char * number)
  183. {
  184. phone_book_entry * index =
  185. &(phone_book_entry)
  186. {
  187. .name.letters[0] = '\0',
  188. .number = make_phone_book_number(number),
  189. };
  190. phone_book_number_sort((phone_book *)pb);
  191. return
  192. bsearch
  193. (
  194. &index,
  195. pb->number_index,
  196. pb->index_size,
  197. sizeof(phone_book_entry *),
  198. phone_book_indexed_compare_number
  199. );
  200. }
  201. const char * phone_book_find_name(const phone_book * pb, const char * number)
  202. {
  203. phone_book_entry * const * found =
  204. phone_book_find_entry_by_number(pb, number);
  205. return found ? (*found)->name.letters : NULL;
  206. }
  207. //-----------------------------------------------
  208. static phone_book_entry * next_unused_entry(phone_book * pb)
  209. {
  210. //TODO: start at pb->index_size
  211. size_t at = 0;
  212. while (at != pb->max_size && pb->entries[at].number.digits[0] != '\0')
  213. at++;
  214. return at != pb->max_size ? &pb->entries[at] : NULL;
  215. }
  216. bool phone_book_add(phone_book * pb, const char * name, const char * number)
  217. {
  218. if (phone_book_find_entry_by_number(pb, number))
  219. return false;
  220. phone_book_entry * found = next_unused_entry(pb);
  221. if (found)
  222. {
  223. *found = make_phone_entry(name, number);
  224. const size_t at = pb->index_size;
  225. pb->name_index[at] = found;
  226. pb->number_index[at] = found;
  227. pb->index_size++;
  228. }
  229. return true;
  230. }
  231. //--------------------------------------------------
  232. void phone_book_print_by_number(const phone_book * pb,
  233. void print(const char * name, const char * number))
  234. {
  235. phone_book_number_sort((phone_book *)pb);
  236. for (size_t at = 0; at != pb->index_size; at++)
  237. {
  238. const phone_book_entry * entry = pb->number_index[at];
  239. print(entry->name.letters, entry->number.digits);
  240. }
  241. }
  242. //--------------------------------------------------
  243. bool phone_book_remove(phone_book * pb, const char * number)
  244. {
  245. phone_book_entry * * found =
  246. phone_book_find_entry_by_number(pb, number);
  247. if (found)
  248. {
  249. const char * name = (*found)->name.letters;
  250. // mark the entry as unused
  251. (*found)->number.digits[0] = '\0';
  252. // remove the ptr from the number index
  253. phone_book_entry * * end_number =
  254. pb->number_index + pb->index_size;
  255. size_t byte_count = (end_number - found) * sizeof *found;
  256. memmove(found, found + 1, byte_count);
  257. // remove from name_index too
  258. found = phone_book_find_entry_by_name(pb, name);
  259. //assert( found );
  260. phone_book_entry * * end_name =
  261. pb->name_index + pb->index_size;
  262. byte_count = (end_name - found) * sizeof *found;
  263. memmove(found, found + 1, byte_count);
  264. pb->index_size--;
  265. }
  266. return found;
  267. }
  268. //--------------------------------------------------
  269. #ifdef DEBUG
  270. #include <stdio.h>
  271. void phone_book_dump(const phone_book * pb)
  272. {
  273. printf("max_size==%zd\n", pb->max_size);
  274. printf("index_size==%zd\n", pb->index_size);
  275. //printf("name_index_is_stale==%s\n", pb->name_index_is_stale ? "true" : "false");
  276. for (size_t at = 0; at != pb->index_size; at++)
  277. {
  278. const size_t pos = (size_t)(pb->name_index[at] - pb->entries);
  279. printf("name_index[%zd]==%zd --> ", at, pos);
  280. const phone_book_entry * pbe = &pb->entries[pos];
  281. printf("number(%s) , name(%s)\n", pbe->number.digits, pbe->name.letters);
  282. }
  283. //printf("number_index_is_stale==%s\n", pb->name_index_is_stale ? "true" : "false");
  284. for (size_t at = 0; at != pb->index_size; at++)
  285. {
  286. const size_t pos = (size_t)(pb->number_index[at] - pb->entries);
  287. printf("number_index[%zd]==%zd --> ", at, pos);
  288. const phone_book_entry * pbe = &pb->entries[pos];
  289. printf("number(%s) , name(%s)\n", pbe->number.digits, pbe->name.letters);
  290. }
  291. printf("-------------------------\n");
  292. }
  293. #endif