perf(xml): linear-time duplicate-child accumulation and tail-recursive filter
ober
88d3d3d13016324a6afd86f346626669bf366539
--- a/lib/jerboa-aws/xml.sls +++ b/lib/jerboa-aws/xml.sls @@ -240,25 +240,31 @@ ;; Set element → list of child hashes [(is-set-element? tag) (map sxml->hash elem-children)] - ;; Container element → hash table + ;; Container element → hash table. Repeated child tags accumulate + ;; onto a reversed per-tag list (O(1) each) and are reversed once, + ;; avoiding the O(n^2) of appending per repeat for elements with + ;; many repeated children (S3 Contents, EC2 reservations). [else - (let ([ht (make-hashtable symbol-hash eq?)]) + (let ([acc (make-hashtable symbol-hash eq?)]) (for-each (lambda (child) (when (pair? child) (let* ([child-tag (strip-ns (car child))] [child-val (sxml->hash child)] - [existing (hashtable-ref ht child-tag #f)]) - (cond - ;; Duplicate → make/extend list - [existing - (if (and (list? existing) (not (hashtable? (car existing)))) - (hashtable-set! ht child-tag (append existing (list child-val))) - (hashtable-set! ht child-tag (list existing child-val)))] - [else - (hashtable-set! ht child-tag child-val)])))) + [existing (hashtable-ref acc child-tag #f)]) + (hashtable-set! acc child-tag + (if existing (cons child-val existing) (list child-val)))))) elem-children) - ht)]))])) + (let ([ht (make-hashtable symbol-hash eq?)] + [keys (hashtable-keys acc)]) + (let loop ([i 0]) + (if (>= i (vector-length keys)) + ht + (let* ([k (vector-ref keys i)] + [vals (reverse (hashtable-ref acc k '()))]) + (hashtable-set! ht k + (if (null? (cdr vals)) (car vals) vals)) + (loop (+ i 1)))))))]))])) ;; Get text content of an SXML element (define (sxml-text elem) @@ -311,17 +317,10 @@ [else (loop (+ i 1))])))) (define (filter pred lst) - (cond - [(null? lst) '()] - [(pred (car lst)) (cons (car lst) (filter pred (cdr lst)))] - [else (filter pred (cdr lst))])) - - (define (hashtable? x) - (or (eq-hashtable? x) - (symbol-hashtable? x) - (and (record? x) ;; catch generic hashtables - (guard (e [#t #f]) - (hashtable-size x) - #t)))) + (let loop ([lst lst] [acc '()]) + (cond + [(null? lst) (reverse acc)] + [(pred (car lst)) (loop (cdr lst) (cons (car lst) acc))] + [else (loop (cdr lst) acc)]))) ) ;; end library