Public API sudah dirancang. Sekarang kita implementasikan setiap fungsi di src/examplelib.c. Aturan yang kita pegang: setiap fungsi memvalidasi argumen sebelum memproses, mengembalikan kode error eksplisit bila input invalid, dan tidak mengakses global state. Fungsi yang murni (tanpa side effect) lebih mudah diuji dan dipakai ulang.
Konsep #
Implementasi library mengikuti beberapa pola:
- Defensive programming — cek NULL pointer dan rentang nilai sebelum akses array.
- Pure function — tidak ada global state, tidak ada side effect selain menulis ke parameter output.
- Single pass algorithm — bila mungkin, lakukan perhitungan dalam satu iterasi.
- Reusable helper — fungsi internal yang dipakai banyak fungsi publik dibuat
static.
Mekanisme kerja: setiap fungsi publik memvalidasi input, memanggil helper bila perlu, lalu menulis hasil ke parameter output. Bila ada error, fungsi langsung return tanpa menulis apa-apa. Pemanggil bisa mengandalkan: bila return code bukan EXLIB_OK, parameter output tidak diubah (kontrak “all-or-nothing”).
Implementasi #
Implementasi penuh examplelib.c (dipotong untuk fokus pada bagian penting):
/* examplelib.c - Implementasi library examplelib */
#include "examplelib.h"
#include <math.h>
#include <string.h>
#include <stdio.h>
const char *exlib_version(void) {
return EXAMPLELIB_VERSION_STRING;
}
const char *exlib_strerror(exlib_status code) {
switch (code) {
case EXLIB_OK: return "sukses";
case EXLIB_ERR_NULL: return "pointer NULL atau n=0";
case EXLIB_ERR_RANGE: return "nilai di luar rentang valid";
case EXLIB_ERR_FORMAT: return "format input tidak valid";
case EXLIB_ERR_OVERFLOW: return "potensi overflow";
case EXLIB_ERR_NOT_FOUND: return "tidak ditemukan";
default: return "kode tidak dikenal";
}
}
exlib_status exlib_mean(const double *data, size_t n, double *out) {
if (data == NULL || out == NULL || n == 0) {
return EXLIB_ERR_NULL;
}
double sum = 0.0;
for (size_t i = 0; i < n; i++) {
sum += data[i];
}
*out = sum / (double)n;
return EXLIB_OK;
}
exlib_status exlib_stddev(const double *data, size_t n, double *out) {
if (data == NULL || out == NULL || n == 0) {
return EXLIB_ERR_NULL;
}
double mean = 0.0;
exlib_status s = exlib_mean(data, n, &mean);
if (s != EXLIB_OK) {
return s;
}
double sum_sq = 0.0;
for (size_t i = 0; i < n; i++) {
double diff = data[i] - mean;
sum_sq += diff * diff;
}
*out = sqrt(sum_sq / (double)n);
return EXLIB_OK;
}
exlib_status exlib_reverse_bytes(uint8_t *data, size_t n) {
if (data == NULL) {
return EXLIB_ERR_NULL;
}
if (n < 2) {
return EXLIB_OK;
}
size_t i = 0;
size_t j = n - 1;
while (i < j) {
uint8_t tmp = data[i];
data[i] = data[j];
data[j] = tmp;
i++;
j--;
}
return EXLIB_OK;
}
exlib_status exlib_format_size(uint64_t bytes, char *out, size_t out_cap) {
if (out == NULL) {
return EXLIB_ERR_NULL;
}
if (out_cap < 16) {
return EXLIB_ERR_RANGE;
}
static const char *units[] = {"B", "KB", "MB", "GB", "TB", "PB"};
static const int n_units = 6;
if (bytes == 0) {
snprintf(out, out_cap, "0.00 B");
return EXLIB_OK;
}
int unit_idx = 0;
double size = (double)bytes;
while (size >= 1024.0 && unit_idx < n_units - 1) {
size /= 1024.0;
unit_idx++;
}
snprintf(out, out_cap, "%.2f %s", size, units[unit_idx]);
return EXLIB_OK;
}
exlib_status exlib_contains(const char *haystack, const char *needle) {
if (haystack == NULL || needle == NULL) {
return EXLIB_ERR_NULL;
}
if (needle[0] == '\0') {
return EXLIB_OK;
}
if (strstr(haystack, needle) != NULL) {
return EXLIB_OK;
}
return EXLIB_ERR_NOT_FOUND;
}Verifikasi implementasi:
/* demo_impl.c - Test implementasi examplelib */
#include <stdio.h>
#include <stdint.h>
#include "examplelib.h"
int main(void) {
/* Test mean. */
double data[] = {1.0, 2.0, 3.0, 4.0, 5.0};
double mean;
exlib_status s = exlib_mean(data, 5, &mean);
printf("mean: s=%d value=%.2f\n", s, mean);
/* Test stddev (populasi). */
double sd;
s = exlib_stddev(data, 5, &sd);
printf("stddev: s=%d value=%.4f\n", s, sd);
/* Test reverse_bytes. */
uint8_t bytes[] = {1, 2, 3, 4, 5};
s = exlib_reverse_bytes(bytes, 5);
printf("reverse: s=%d data=", s);
for (int i = 0; i < 5; i++) {
printf("%d ", bytes[i]);
}
printf("\n");
/* Test format_size. */
char buf[32];
s = exlib_format_size(0, buf, sizeof(buf));
printf("format 0: s=%d '%s'\n", s, buf);
s = exlib_format_size(1024ULL, buf, sizeof(buf));
printf("format 1024: s=%d '%s'\n", s, buf);
s = exlib_format_size(1572864ULL, buf, sizeof(buf));
printf("format 1.5MB: s=%d '%s'\n", s, buf);
s = exlib_format_size(1099511627776ULL, buf, sizeof(buf));
printf("format 1TB: s=%d '%s'\n", s, buf);
/* Test contains. */
s = exlib_contains("hello world", "world");
printf("contains 'world': s=%d\n", s);
s = exlib_contains("hello world", "xyz");
printf("contains 'xyz': s=%d\n", s);
/* Test error cases. */
s = exlib_mean(NULL, 5, &mean);
printf("mean(NULL): s=%d (%s)\n", s, exlib_strerror(s));
s = exlib_format_size(100, buf, 5);
printf("format buf kecil: s=%d (%s)\n", s, exlib_strerror(s));
return 0;
}Output yang Dihasilkan:
mean: s=0 value=3.00
stddev: s=0 value=1.4142
reverse: s=0 data=5 4 3 2 1
format 0: s=0 '0.00 B'
format 1024: s=0 '1.00 KB'
format 1.5MB: s=0 '1.50 MB'
format 1TB: s=0 '1.00 TB'
contains 'world': s=0
contains 'xyz': s=-5
mean(NULL): s=-1 (pointer NULL atau n=0)
format buf kecil: s=-2 (nilai di luar rentang valid)Mari kita telaah baris-baris kode di atas:
- Baris 6-8 (
exlib_version): Mengembalikan string statis dari macroEXAMPLELIB_VERSION_STRING. String statis aman dipakai karena tidak bergantung pada state runtime. - Baris 10-21 (
exlib_strerror): Switch case yang menerjemahkan kode error ke pesan.defaultmenangani kode tidak dikenal — defensif terhadap nilai enum di luar rentang yang didefinisikan. - Baris 23-31 (
exlib_mean): Validasi tiga argumen:datatidak NULL,outtidak NULL,n > 0. Bila ada yang gagal, return error tanpa memproses. Kontrak: bila return != OK,*outtidak diubah. - Baris 33-46 (
exlib_stddev): Panggilexlib_meanuntuk dapat mean, lalu iterasi kedua untuk hitung variance. Pattern “komposisi” — fungsi publik memanggil fungsi publik lain — mengurangi duplikasi logika. - Baris 48-65 (
exlib_reverse_bytes): Dua pointeri(depan) danj(belakang) bergerak saling mendekati. Tukar byte di posisi keduanya. Loop berhenti saati >= j. Algoritma in-place ini membutuhkanO(n/2)swap. - Baris 67-87 (
exlib_format_size): Pilih unit yang sesuai dengan iterasi pembagian 1024. Setiap iterasi, ukuran dibagi 1024 dan unit_idx naik. Batasunit_idx < n_units - 1mencegah akses array out-of-bounds. - Baris 89-100 (
exlib_contains): Cek NULL dulu, lalu needle kosong (selalu match). Untuk kasus umum, pakaistrstrdari<string.h>yang sudah dioptimasi compiler. - Baris 21 (
return "kode tidak dikenal"): Default case. Walaupun enum sudah lengkap, kode eksternal bisa melewatkan integer acak cast keexlib_status. Default memberi pesan yang masuk akal daripada crash.
Kode Lengkap #
Kode lengkap untuk examplelib.c ada di direktori src/ proyek. Semua fungsi publik mengikuti pola yang sama: validasi input → proses → tulis output → return kode.
Pengujian #
Selain demo di atas, test edge case:
exlib_meandengan n=0 → error NULL.exlib_meandengan data NULL → error NULL.exlib_stddevdengan data konstan → stddev harus 0.exlib_reverse_bytesdengan n=0 atau n=1 → OK, no-op.exlib_format_sizedenganout_cap5 → error RANGE (terlalu kecil).exlib_format_sizedengan bytes = UINT64_MAX → harus format sebagai PB.exlib_containsdengan needle kosong → OK (selalu match).
gcc -std=c11 -Wall -Wextra -Iinclude -o /tmp/demo_impl \
demo_impl.c src/examplelib.c -lm
/tmp/demo_impl