;;; -*- Mode:LISP; Package: OBJ; Base:10; Readtable:CL; Syntax: Common-Lisp -*- ;;; ;;; ****************************************************************************** ;;; Copyright (c) 1984, 1985 Gary L. Drescher. All rights reserved. ;;; Licensed to and distributed by Lisp Machine, Inc. ;;; ;;; Use and copying of this software and preparation of derivative works based ;;; upon this software are permitted. Any distribution of this software or ;;; derivative works must comply with all applicable United States export control ;;; laws. ;;; ;;; This software is made available AS IS, and no warranty is made about the ;;; software, its performance, or its conformity to any specification. ;;; ;;; Any person obtaining a copy of this software is requested to send her name ;;; and post office or electronic mail address to: ;;; ObjectLISP Coordinator ;;; c/o User Interface Group ;;; Lisp Machine, Inc. ;;; 1000 Massachusetts Ave. ;;; Cambridge, Ma 02138 ;;; ;;; Suggestions, comments, and requests for improvements are also welcome. ;;; ****************************************************************************** (in-package "OBJ") ;; One of max-load, inv-load should be 1, the other an integer. ;; The real max load factor is max-load / inv-load. (defparameter *maximum-load-factor 2) (defparameter *inverse-load-factor 1) (defparameter *minimum-hash-size 32.) (defparameter *alist-to-hash-threshold 32.) (defparameter *default-alist-to-hash-threshold 32.) (defparameter *hash-to-alist-threshold -1) ; This file supports special-purpose tables with / entries, ; where a contains an entry's key & associated hashcode. (defstruct (basic-link (:conc-name nil) #+lambda (:callable-constructors nil) (:constructor make-basic-link (link-key link-index))) link-index link-key ) ;Table format: (COUNT . ALIST) or (COUNT MASK . HASH-TABLE) ;ALIST ROUTINES ;Alist entry: (VALUE . LINK) ; Inputs must not repeat a given link more than once. (defun alist-make-table (link-val-pairs &aux table) (nloop (stop-if (null link-val-pairs)) (for link (pop link-val-pairs)) (for val (pop link-val-pairs)) (push (cons val link) table)) table) (defsubst alist-lookup (link table) (rassq link (cdr table))) (defsubst alist-insert-new-ok-size (link val table) (push (cons val link) (cdr table))) (defsubst alist-remove (link table) (setf (cdr table) (remq (rassq link (cdr table)) (cdr table)))) ;HASH ROUTINES ;Hash table entry: (VALUE . LINK) (defmacro hash-slot-contents (array mask ix) `(fast-aref ,array (logand ,mask ,ix))) (microdefun make-hash-cons (number-of-entries) (let ((size (max *minimum-hash-size (nloop (with base (* 2 number-of-entries)) (for i 1 (* 2 i)) (stop-unless (< i base)) (finally i))))) (cons (1- size) (make-array size :adjustable nil)))) (defun hash-make-table (link-val-pairs &aux hashcons mask array) (setq hashcons (make-hash-cons (floor (length link-val-pairs) 2)) mask (car hashcons) array (cdr hashcons)) (nloop (stop-if (null link-val-pairs)) (for link (pop link-val-pairs)) (for val (pop link-val-pairs)) (push (cons val link) (hash-slot-contents array mask (link-index link)))) (cons mask array)) (defsubst hash-lookup (link table) (rassq link (hash-slot-contents (cddr table) (cadr table) (link-index link)))) (microdefun double-table-size (table) (let* ((old-mask (cadr table)) (new-mask (1- (* 2 (1+ old-mask)))) (old-array (cddr table)) (NEW-ARRAY (MAKE-ARRAY (1+ NEW-MASK) :ADJUSTABLE NIL))) (RPLACD (CDR TABLE) NEW-ARRAY) ; (adjust-array array #+lambda (ncons (1+ new-mask)) #-lambda (1+ new-mask)) (rplaca (cdr table) new-mask) ;; Now xfer old array contents to new. (nloop (inc-til i 0 old-mask) (for contents (fast-aref OLD-array i)) (nloop (for-in entry contents) (for ix (link-index (cdr entry))) ; (unless (= (logand ix old-mask) (logand ix new-mask)) ; (setq contents (remq entry contents)) (push entry (fast-aref NEW-array (logand ix new-mask))) )))) ; (setf (fast-aref array i) contents)))) (microdefun hash-insert-new (link val table) (incf (car table)) (if (> (* *inverse-load-factor (car table)) (* *maximum-load-factor (1+ (cadr table)))) (double-table-size table)) (push (cons val link) (hash-slot-contents (cddr table) (cadr table) (link-index link)))) (defsubst hash-insert (link val table) (let ((entry (rassq link (hash-slot-contents (cddr table) (cadr table) (link-index link))))) (if entry (rplaca entry val) (hash-insert-new link val table)))) (microdefun hash-remove (link table) (let ((mask (cadr table)) (array (cddr table)) (index (link-index link))) (setf (hash-slot-contents array mask INDEX) (remq (rassq link (hash-slot-contents array mask INDEX)) (hash-slot-contents array mask INDEX))))) (defun hash-table-map (fcn array) (nloop (inc-nottil i 0 (array-total-size array)) (mapc fcn (fast-aref array i)))) (defun hash-table-map-return (fcn array) (nloop (inc-nottil i 0 (array-total-size array)) (collect* (mapcar fcn (fast-aref array i))))) ;CONVERSION ROUTINES (microdefun alist-to-hash (table) (let* ((alist (cdr table)) (hc (make-hash-cons (length alist))) (array (cdr hc))) (nloop (for-in entry (cdr table)) (push entry (hash-slot-contents array (car hc) (link-index (cdr entry))))) (rplacd table hc))) (microdefun alist-insert-new (link val table) (cond ((>= (car table) *alist-to-hash-threshold) (alist-to-hash table) (hash-insert-new link val table)) (t (incf (car table)) (alist-insert-new-ok-size link val table)))) (microdefun alist-insert (link val table) (let ((cons (rassq link (cdr table)))) (if cons (rplaca cons val) (alist-insert-new link val table)))) ;GENERIC ROUTINES (defun make-table (&rest link-val-pairs &aux (count (floor (length link-val-pairs) 2))) (if (> count *alist-to-hash-threshold) (cons count (hash-make-table link-val-pairs)) (cons count (alist-make-table link-val-pairs)))) (defsubst table-count (table) (car table)) (defsubst alist? (table) (not (numberp (cadr table)))) ;Returns an assq-cons. (microdefun lookup (link table) (if (alist? table) (alist-lookup link table) (hash-lookup link table))) (microdefun insert-new (link val table) (if (alist? table) (alist-insert-new link val table) (hash-insert-new link val table))) (microdefun insert (link val table) (if (alist? table) (alist-insert link val table) (hash-insert link val table))) (microdefun table-remove (link table) (if (alist? table) (alist-remove link table) (hash-remove link table))) ;FCN should take one input, a table entry, (VALUE . LINK). (defun table-map (fcn table) (if (alist? table) (mapc fcn (cdr table)) (hash-table-map fcn (cddr table)))) (defun table-map-return (fcn table) (if (alist? table) (mapcar fcn (cdr table)) (hash-table-map-return fcn (cddr table))))