The Easiest Way to Save and Share Code Snippets on the web

Buddy System in Scheme

lisp

posted: Apr, 27th 2012 | jump to bottom

;; Buddy System in Scheme
;; Author: Maurizio Giordano
;; Install/Run:
;; a) download/install Racket Scheme (5.0 or later)
;; b) run "drracket"
;; c) open this file in drracket
;; d) click "Run" to load the source
;; e) play with the Buddy System API:
;;    i) alloc pages with (alloc_pages order)
;;    ii) free pages with (free_pages page-index order)
;;    iii) display BS config with (draw_memmap_blocks free_area)
;;
 
;; Import racket libraries
(require racket/base)
(require racket/list)
(require racket/vector)
(require 2htdp/image)
 
;; Buddy SYstem Graphic library and globals
(define dx 10)
(define dy 80)
(define delta 4)
(define penwidth 3)
 
(define (page-free)
  (overlay/align "middle" "middle"
           (rectangle dx dy "solid" "white")
           (rectangle (+ dx delta) (+ dy delta) "outline" "black")))
 
(define (page-notfree)
  (overlay/align "middle" "middle"
           (rectangle 10 80 "solid" "pink")
           (rectangle 14 84 "outline" "black")))
 
 
(define (draw_block order index mem)
  (overlay/xy (rectangle (* (expt 2 order) (+ dx delta)) (+ dy delta) "outline" 
                         ;(vector-ref block-colors order)
                         (make-pen (vector-ref block-colors order) 5 "long-dash" "round" "round")
                         ) 
              (- (* index (+ dx delta))) 
              0 mem))
 
(define (draw_memmap mem)
  (if (= 0 (vector-length mem)) 
      #f
      (let loop ((i (vector-length mem)))
        (let ((page (if (= (vector-ref mem (sub1 i)) 0) (page-free) (page-notfree))))
          (if (= i 1) 
              page
              (overlay/xy (loop (sub1 i)) (* (- i 1) (+ dx delta)) 0 page))))))
 
(define (draw_memmap_blocks farea)
  (if (= 0 (vector-length farea)) 
      #f
     (let ((image (draw_memmap mem_map)))
        (let loop ((i (vector-length farea)))
           (cond ((> i 0)
                  (let inloop ((l (vector-ref farea (sub1 i))))
                     (cond ((not (null? l))
                            (set! image (draw_block (sub1 i) (car l) image))
                            (inloop (cdr l)))))
                  (loop (sub1 i)))))
        image)))
 
(define block-colors (vector "red" "orange" "yellow" "green" "blue" "purple" "cyan"))
 
;; Buddy System globals
(define maxorder 5)    ;; max order
(define free_area (make-vector (add1 maxorder) '()))  ;; free_area vector
;; init BS with two free blocks of 32 pages
(vector-set! free_area maxorder (list 0 (expt 2 maxorder)))
;; init memory map with 64 free pages 
(define mem_map (make-vector (* 2 (expt 2 maxorder)) 0))
 
(define (getblock order)
  (let*-values (((first rest) (split-at (vector-ref free_area order) 1))
                ((page) (car first)))
    (values page rest)))
 
;; BS - find the requested block or split larger blocks
(define (rmqueue order)
  (let loop1 ((i order))
    (if (>= i (vector-length free_area)) 
        null                 ; no block could be found
        (if (null? (vector-ref free_area i))
            (loop1 (add1 i))
            (let*-values (((page rest) (getblock i)))
               (vector-set! free_area i rest)
               (let loop2 ((j i))
                  (if (= j order)
                      page
                      (begin (vector-set! free_area (sub1 j) (list (+ page (expt 2 (sub1 j)))))
                             (loop2 (sub1 j))))))))))
 
;; given a block of pages, it finds the "buddy" block
(define (buddy-of page order) 
  (if (= 0 (modulo page (expt 2 (+ order 1))))
      (+ page (expt 2 order))   ;; the buddy block is over the page-block 
      (- page (expt 2 order)))) ;; the buddy block is below the page-block  
 
(define (check_and_clear_pages page order)
  (for ((i (in-range (expt 2 order))))
    (cond ((= 0 (vector-ref mem_map (+ page i)))
        (error "block has some free pages"))))
  (for ((i (in-range (expt 2 order))))
    (vector-set! mem_map (+ page i) 0)))
 
;;
;; BS API
;;
;; (alloc_pages order) -> integer?|null?
;;   order : integer?
;; return: the page index in case of allocation of block of 2^order pages 
;;         otherwise null (empty list) 
 
(define (alloc_pages order)
  (let ((page (rmqueue order)))
    (if (null? page)
        (printf "the block cannot be allocated\n")
        (for ((i (in-range (expt 2 order))))
          (vector-set! mem_map (+ page i) 1)))
    page)) 
 
;; (free_pages page order) -> void?
;;   page  : integer?
;;   order : integer?
;; return: void?
;;   if the deallocation succeded, the block of 2^order pages starting from index "page" 
;;   is freed, otherwise an error is printed 
(define (free_pages page order)
  (check_and_clear_pages page order)       ;; check if pages are allocated and free them
  (let loop ((i order) (1stpage page))     ;; iterate from oder up
    (cond ((< i maxorder) (printf "order=~s\n" i)
           (let ((buddy (buddy-of 1stpage i))  ;; compute the buddy
                 (blocklist (vector-ref free_area i)))  ;; take the list of blocks
             (printf "MEMBER ~s\n" (member buddy blocklist))
             (cond ((member buddy blocklist)
                    (vector-set! free_area i (remove buddy blocklist)) ;; buddy in list remove it
                    (loop (add1 i) (if (> buddy 1stpage) 1stpage buddy)))
                   (else (printf "final block is ~s-~s\n" 1stpage (+ 1stpage (expt 2 i)))
                         (vector-set! free_area i
                           (sort (append (list 1stpage) (vector-ref free_area i)) <=))))))
          (else (printf "final block is ~s-~s\n" 1stpage (+ 1stpage (expt 2 i)))
                (vector-set! free_area i
                      (sort (append (list 1stpage) (vector-ref free_area i)) <=))))))
 
9050 views