← Back to Reports Index

CELLO Evaluation Report: hashmap

Generated: 2026-02-15T23:51:12.561669

Original C Code (3297 bytes)

/* Simple hash map implementation - fundamental data structure */

#include <stdlib.h>
#include <string.h>

#define HASHMAP_INITIAL_CAPACITY 16

typedef struct HashMapEntry {
    char* key;
    void* value;
    struct HashMapEntry* next;
} HashMapEntry;

typedef struct {
    HashMapEntry** buckets;
    size_t capacity;
    size_t size;
} HashMap;

/**
 * Simple hash function (djb2)
 */
static unsigned long hash_string(const char* str) {
    unsigned long hash = 5381;
    int c;
    while ((c = *str++)) {
        hash = ((hash << 5) + hash) + c;
    }
    return hash;
}

/**
 * Create a new hash map
 */
HashMap* hashmap_new(void) {
    HashMap* map = (HashMap*)malloc(sizeof(HashMap));
    if (!map) return NULL;
    
    map->capacity = HASHMAP_INITIAL_CAPACITY;
    map->size = 0;
    map->buckets = (HashMapEntry**)calloc(map->capacity, sizeof(HashMapEntry*));
    
    if (!map->buckets) {
        free(map);
        return NULL;
    }
    
    return map;
}

/**
 * Insert or update a key-value pair
 */
int hashmap_put(HashMap* map, const char* key, void* value) {
    if (!map || !key) return -1;
    
    unsigned long hash = hash_string(key);
    size_t index = hash % map->capacity;
    
    // Check if key exists
    HashMapEntry* entry = map->buckets[index];
    while (entry) {
        if (strcmp(entry->key, key) == 0) {
            entry->value = value;
            return 0;
        }
        entry = entry->next;
    }
    
    // Create new entry
    HashMapEntry* new_entry = (HashMapEntry*)malloc(sizeof(HashMapEntry));
    if (!new_entry) return -1;
    
    new_entry->key = strdup(key);
    if (!new_entry->key) {
        free(new_entry);
        return -1;
    }
    
    new_entry->value = value;
    new_entry->next = map->buckets[index];
    map->buckets[index] = new_entry;
    map->size++;
    
    return 0;
}

/**
 * Get value for a key
 */
void* hashmap_get(HashMap* map, const char* key) {
    if (!map || !key) return NULL;
    
    unsigned long hash = hash_string(key);
    size_t index = hash % map->capacity;
    
    HashMapEntry* entry = map->buckets[index];
    while (entry) {
        if (strcmp(entry->key, key) == 0) {
            return entry->value;
        }
        entry = entry->next;
    }
    
    return NULL;
}

/**
 * Remove a key-value pair
 */
int hashmap_remove(HashMap* map, const char* key) {
    if (!map || !key) return -1;
    
    unsigned long hash = hash_string(key);
    size_t index = hash % map->capacity;
    
    HashMapEntry** indirect = &map->buckets[index];
    HashMapEntry* entry = *indirect;
    
    while (entry) {
        if (strcmp(entry->key, key) == 0) {
            *indirect = entry->next;
            free(entry->key);
            free(entry);
            map->size--;
            return 0;
        }
        indirect = &entry->next;
        entry = entry->next;
    }
    
    return -1;
}

/**
 * Free hash map and all entries
 */
void hashmap_free(HashMap* map) {
    if (!map) return;
    
    for (size_t i = 0; i < map->capacity; i++) {
        HashMapEntry* entry = map->buckets[i];
        while (entry) {
            HashMapEntry* next = entry->next;
            free(entry->key);
            free(entry);
            entry = next;
        }
    }
    
    free(map->buckets);
    free(map);
}

CLAUDE Transpilation

Overall Score: 72/100
Dimension Score Max Details
Compilation 25 25 ✓ Compiles successfully
Safety 20 20 ✓ No unsafe blocks | ✓ Uses Result for error handling | ✓ Uses Option for nullable values | ✓ Smart pointer usage
Quality 12 20 ✓ Snake_case naming | ✓ Well documented (11 doc comments) | ✓ Proper type definitions
Correctness 0 15 No correctness indicators
Maintainability 5 10 ✓ Clear public API (9 items) | ✓ Uses constants
Performance 10 10 ✓ Minimal cloning | ✓ Reference usage

✅ Compilation: PASSED

Generated Rust Code (4107 bytes)

use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};

const HASHMAP_INITIAL_CAPACITY: usize = 16;

/// Entry in the hash map containing a key-value pair and a link to the next entry
struct HashMapEntry<T> {
    key: String,
    value: T,
    next: Option<Box<HashMapEntry<T>>>,
}

/// Simple hash map implementation using separate chaining for collision resolution
pub struct HashMap<T> {
    buckets: Vec<Option<Box<HashMapEntry<T>>>>,
    capacity: usize,
    size: usize,
}

impl<T> HashMap<T> {
    /// Create a new hash map with initial capacity
    pub fn new() -> Self {
        let capacity = HASHMAP_INITIAL_CAPACITY;
        let mut buckets = Vec::with_capacity(capacity);
        for _ in 0..capacity {
            buckets.push(None);
        }
        
        HashMap {
            buckets,
            capacity,
            size: 0,
        }
    }

    /// Simple hash function (djb2)
    fn hash_string(s: &str) -> u64 {
        let mut hash: u64 = 5381;
        for c in s.bytes() {
            hash = hash.wrapping_shl(5).wrapping_add(hash).wrapping_add(c as u64);
        }
        hash
    }

    /// Insert or update a key-value pair
    pub fn put(&mut self, key: &str, value: T) -> Result<(), &'static str> {
        let hash = Self::hash_string(key);
        let index = (hash % self.capacity as u64) as usize;

        // Check if key exists and update
        let mut current = &mut self.buckets[index];
        while let Some(entry) = current {
            if entry.key == key {
                entry.value = value;
                return Ok(());
            }
            current = &mut entry.next;
        }

        // Create new entry at the beginning of the chain
        let new_entry = Box::new(HashMapEntry {
            key: key.to_string(),
            value,
            next: self.buckets[index].take(),
        });

        self.buckets[index] = Some(new_entry);
        self.size += 1;

        Ok(())
    }

    /// Get a reference to the value for a key
    pub fn get(&self, key: &str) -> Option<&T> {
        let hash = Self::hash_string(key);
        let index = (hash % self.capacity as u64) as usize;

        let mut current = &self.buckets[index];
        while let Some(entry) = current {
            if entry.key == key {
                return Some(&entry.value);
            }
            current = &entry.next;
        }

        None
    }

    /// Get a mutable reference to the value for a key
    pub fn get_mut(&mut self, key: &str) -> Option<&mut T> {
        let hash = Self::hash_string(key);
        let index = (hash % self.capacity as u64) as usize;

        let mut current = &mut self.buckets[index];
        while let Some(entry) = current {
            if entry.key == key {
                return Some(&mut entry.value);
            }
            current = &mut entry.next;
        }

        None
    }

    /// Remove a key-value pair, returning the value if it existed
    pub fn remove(&mut self, key: &str) -> Result<T, &'static str> {
        let hash = Self::hash_string(key);
        let index = (hash % self.capacity as u64) as usize;

        let mut current = &mut self.buckets[index];

        while let Some(entry) = current {
            if entry.key == key {
                let mut removed_entry = current.take().unwrap();
                *current = removed_entry.next.take();
                self.size -= 1;
                return Ok(removed_entry.value);
            }
            current = &mut entry.next;
        }

        Err("Key not found")
    }

    /// Get the number of entries in the map
    pub fn size(&self) -> usize {
        self.size
    }

    /// Get the capacity of the map
    pub fn capacity(&self) -> usize {
        self.capacity
    }

    /// Check if the map is empty
    pub fn is_empty(&self) -> bool {
        self.size == 0
    }
}

impl<T> Default for HashMap<T> {
    fn default() -> Self {
        Self::new()
    }
}

impl<T> Drop for HashMap<T> {
    fn drop(&mut self) {
        // Rust automatically cleans up all entries when buckets are dropped
    }
}

GEMINI Transpilation

Overall Score: 74/100
Dimension Score Max Details
Compilation 25 25 ✓ Compiles successfully
Safety 20 20 ✓ No unsafe blocks | ✓ Uses Result for error handling | ✓ Uses Option for nullable values | ✓ Smart pointer usage
Quality 14 20 ✓ Snake_case naming | ✓ Well documented (8 doc comments) | ✓ Proper type definitions | ✓ Implementation blocks
Correctness 3 15 ✓ Pattern matching
Maintainability 4 10 ✓ Uses constants | ✓ Type aliases
Performance 8 10 ✓ Minimal cloning

✅ Compilation: PASSED

Generated Rust Code (5221 bytes)

use std::collections::hash_map::DefaultHasher;
use std::error::Error;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::ptr;

const HASHMAP_INITIAL_CAPACITY: usize = 16;

#[derive(Debug)]
struct HashMapError(String);

impl fmt::Display for HashMapError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "HashMap Error: {}", self.0)
    }
}

impl Error for HashMapError {}

type Result<T> = std::result::Result<T, HashMapError>;

#[derive(Debug)]
struct HashMapEntry<K, V> {
    key: K,
    value: V,
    next: Option<Box<HashMapEntry<K, V>>>,
}

#[derive(Debug)]
struct HashMap<K, V> {
    buckets: Vec<Option<Box<HashMapEntry<K, V>>>>,
    capacity: usize,
    size: usize,
}

impl<K, V> HashMap<K, V>
where
    K: Eq + Hash + Clone,
{
    /// Creates a new hash map with the initial capacity.
    fn new() -> Self {
        let capacity = HASHMAP_INITIAL_CAPACITY;
        let buckets = vec![None; capacity];
        HashMap {
            buckets,
            capacity,
            size: 0,
        }
    }

    /// Generates a hash value for the given key.
    fn hash_string(key: &K) -> u64 {
        let mut hasher = DefaultHasher::new();
        key.hash(&mut hasher);
        hasher.finish()
    }

    /// Inserts a key-value pair into the hash map.
    fn put(&mut self, key: K, value: V) -> Result<()> {
        let hash = Self::hash_string(&key);
        let index = (hash % self.capacity as u64) as usize;

        let bucket = &mut self.buckets[index];

        match bucket {
            Some(entry) => {
                let mut current = entry;
                loop {
                    if current.key == key {
                        current.value = value;
                        return Ok(());
                    }
                    match &mut current.next {
                        Some(next_entry) => {
                            current = next_entry;
                        }
                        None => {
                            break;
                        }
                    }
                }

                let new_entry = HashMapEntry {
                    key: key.clone(),
                    value,
                    next: None,
                };
                current.next = Some(Box::new(new_entry));
            }
            None => {
                let new_entry = HashMapEntry {
                    key: key.clone(),
                    value,
                    next: None,
                };
                *bucket = Some(Box::new(new_entry));
            }
        }

        self.size += 1;
        Ok(())
    }

    /// Retrieves the value associated with the given key.
    fn get(&self, key: &K) -> Option<&V> {
        let hash = Self::hash_string(key);
        let index = (hash % self.capacity as u64) as usize;

        let bucket = &self.buckets[index];

        match bucket {
            Some(entry) => {
                let mut current = entry;
                loop {
                    if current.key == *key {
                        return Some(&current.value);
                    }
                    match &current.next {
                        Some(next_entry) => {
                            current = next_entry;
                        }
                        None => {
                            return None;
                        }
                    }
                }
            }
            None => None,
        }
    }

    /// Removes a key-value pair from the hash map.
    fn remove(&mut self, key: &K) -> Result<()> {
        let hash = Self::hash_string(key);
        let index = (hash % self.capacity as u64) as usize;

        let bucket = &mut self.buckets[index];

        match bucket {
            Some(entry) => {
                if entry.key == *key {
                    *bucket = entry.next.take();
                    self.size -= 1;
                    return Ok(());
                } else {
                    let mut current = entry;
                    loop {
                        match &mut current.next {
                            Some(next_entry) => {
                                if next_entry.key == *key {
                                    current.next = next_entry.next.take();
                                    self.size -= 1;
                                    return Ok(());
                                } else {
                                    current = next_entry;
                                }
                            }
                            None => {
                                return Err(HashMapError("Key not found".to_string()));
                            }
                        }
                    }
                }
            }
            None => Err(HashMapError("Key not found".to_string())),
        }
    }

    /// Frees the hash map and all its entries.
    fn free(&mut self) {
        self.buckets.clear();
        self.size = 0;
        self.capacity = 0;
    }

    /// Returns the number of elements in the hash map.
    fn len(&self) -> usize {
        self.size
    }

    /// Returns if the hash map is empty
    fn is_empty(&self) -> bool {
        self.size == 0
    }
}

Model Comparison

Model Total Compilation Safety Quality Correctness Maintainability Performance
CLAUDE 72/100 25/25 20/20 12/20 0/15 5/10 10/10
GEMINI 74/100 25/25 20/20 14/20 3/15 4/10 8/10

Evaluation Methodology

CELLO evaluates transpilation quality across 6 dimensions:

Total possible score: 100 points