LCOV - code coverage report
Current view: top level - src - dictionary_m.f90 (source / functions) Hit Total Coverage
Test: fht_coverage.info Lines: 70 70 100.0 %
Date: 2018-03-20 Functions: 7 10 70.0 %

          Line data    Source code
       1             : !> \file dictionary_m.f90
       2             : !! \brief Module file for dictionary_t
       3             : 
       4             : !> Dictionary type that uses strings for the keys and values
       5             : !!
       6             : !! Design:
       7             : !!  - djb2 hash function (D. J. Bernstein, see http://www.cse.yorku.ca/~oz/hash.html)
       8             : !!  - The strings are all "character(len=:), allocatable" variables
       9             : !!  - There is no linked list nor pointers, only allocatable arrays for the dynamic data structure
      10             : !!  - set rewrites existing entries without complaining
      11             : 
      12             : module dictionary_m
      13             :   implicit none
      14             : 
      15             :   private
      16             : 
      17             :   public :: dictionary_t
      18             : 
      19             :   !> Single entry in the dictionary
      20             :   type entry_t
      21             :      character(len=:), allocatable :: key
      22             :      character(len=:), allocatable :: value
      23             :   end type entry_t
      24             : 
      25             :   !> A bucket contains several entries
      26             :   type bucket_t
      27             :      type(entry_t), allocatable :: entries(:)
      28             :      integer :: current_size = 0
      29             :      integer :: current_idx = 0
      30             :    contains
      31             :      procedure :: find
      32             :   end type bucket_t
      33             : 
      34             :   !> The dictionary contains dict_size buckets (defined at run time)
      35             :   type dictionary_t
      36             :      type(bucket_t), allocatable :: buckets(:)
      37             :      integer :: dict_size = 0
      38             :    contains
      39             :      procedure :: djb2
      40             :      procedure :: set
      41             :      procedure :: get
      42             :      procedure :: init
      43             :      procedure :: show
      44             :   end type dictionary_t
      45             : 
      46             :   integer, parameter :: BUCKET_EMPTY = -2
      47             :   integer, parameter :: BUCKET_ENTRY_NOT_FOUND = -4
      48             : 
      49             : contains
      50             : 
      51             :   !> djb2 hash function
      52             :   !!
      53             :   !! \param this the dictionary_t object
      54             :   !! \param s a string
      55             :   !!
      56             :   !! \return the hash value between 0 and dict_size-1
      57          19 :   function djb2(this, s) result(r)
      58             :     class(dictionary_t), intent(in) :: this
      59             :     character(len=*), intent(in) :: s
      60             :     integer :: r
      61             : 
      62             :     integer :: i, l
      63             : 
      64          19 :     l = len(s)
      65             : 
      66          19 :     r = 5381
      67             : 
      68          97 :     do i = 1, l
      69          78 :        r = r*33 + ichar(s(i:i))
      70             :     end do
      71             : 
      72          19 :     r = modulo(r, this%dict_size)
      73             : 
      74          38 :   end function djb2
      75             : 
      76             :   !> Add or replace an entry in the dictionary
      77             :   !!
      78             :   !! \param this the dictionary_t object
      79             :   !! \param k the key
      80             :   !! \param v the value
      81          13 :   subroutine set(this, k, v)
      82             :     class(dictionary_t), intent(inout) :: this
      83             :     character(len=*), intent(in) :: k
      84             :     character(len=*), intent(in) :: v
      85             : 
      86          13 :     type(bucket_t) :: tmp_bucket
      87             : 
      88             :     integer :: h, i, b_idx
      89             : 
      90          13 :     h = this%djb2(k) + 1
      91             : 
      92          13 :     b_idx = this%buckets(h)%find(k)
      93             : 
      94          13 :     if (b_idx == BUCKET_EMPTY) then
      95             :        ! allocate bucket for 1 entry
      96             :        ! also, means we can take the first entry
      97          11 :        allocate(this%buckets(h)%entries(1))
      98          11 :        this%buckets(h)%current_size = 1
      99          11 :        this%buckets(h)%current_idx = 1
     100          11 :        b_idx = 1
     101          11 :        this%buckets(h)%entries(1)%key = trim(k)
     102          11 :        this%buckets(h)%entries(1)%value = trim(v)
     103             :        ! the values are registered, exit
     104          11 :        return
     105             :     end if
     106             : 
     107           2 :     if (b_idx == BUCKET_ENTRY_NOT_FOUND) then
     108             :        ! copy and grow bucket entries
     109             :        
     110           1 :        allocate(tmp_bucket%entries(this%buckets(h)%current_size + 1))
     111           1 :        tmp_bucket%current_size = this%buckets(h)%current_size + 1
     112           1 :        tmp_bucket%current_idx = this%buckets(h)%current_idx + 1
     113             : 
     114           2 :        do i = 1, this%buckets(h)%current_size
     115           1 :           tmp_bucket%entries(i)%key = this%buckets(h)%entries(i)%key
     116           1 :           tmp_bucket%entries(i)%value = this%buckets(h)%entries(i)%value
     117             :        end do
     118             : 
     119           1 :        deallocate(this%buckets(h)%entries)
     120           1 :        allocate(this%buckets(h)%entries, source=tmp_bucket%entries)
     121           1 :        deallocate(tmp_bucket%entries)
     122             : 
     123           1 :        this%buckets(h)%current_size = tmp_bucket%current_size
     124           1 :        this%buckets(h)%current_idx = tmp_bucket%current_idx
     125           1 :        b_idx = this%buckets(h)%current_idx
     126             :     end if
     127             : 
     128           2 :     if (b_idx > 0) then
     129           2 :        this%buckets(h)%entries(b_idx)%key = k
     130           2 :        this%buckets(h)%entries(b_idx)%value = v
     131             :     end if
     132             : 
     133          26 :   end subroutine set
     134             : 
     135             :   !> Initialize a dictionary object
     136             :   !!
     137             :   !! \param this the dictionary_t object
     138             :   !! \param dict_size the size of the hash table
     139           3 :   subroutine init(this, dict_size)
     140             :     class(dictionary_t), intent(out) :: this
     141             :     integer, intent(in) :: dict_size
     142             : 
     143           3 :     allocate(this%buckets(dict_size))
     144           3 :     this%dict_size = dict_size
     145             : 
     146           3 :   end subroutine init
     147             : 
     148             :   !> Display the content of a dictionary
     149             :   !!
     150             :   !! \param this the dictionary_t object
     151           1 :   subroutine show(this)
     152             :     class(dictionary_t), intent(in) :: this
     153             : 
     154             :     integer :: i, j, s
     155             :     integer :: n
     156             : 
     157           1 :     n = 0
     158          11 :     do i = 1, this%dict_size
     159          10 :        s = this%buckets(i)%current_idx
     160          10 :        if (s > 0) then
     161           5 :              write(*,*) 'bucket   : ', i, ' size ', s
     162          11 :           do j = 1, s
     163           6 :              write(*,*) 'key      : ', this%buckets(i)%entries(j)%key
     164           6 :              write(*,*) 'value    : ', this%buckets(i)%entries(j)%value
     165             :           end do
     166             :        end if
     167             :     end do
     168             : 
     169           1 :   end subroutine show
     170             : 
     171             :   !> Find the "in-bucket" index for a given key
     172             :   !!
     173             :   !! Negative return values correspond to module-defined return codes.
     174             :   !!
     175             :   !! \param this the bucket_t object
     176             :   !! \param k the key
     177             :   !!
     178             :   !! \return the index (1-based) of the key in the bucket or a return code
     179          19 :   function find(this, k) result(r)
     180             :     class(bucket_t), intent(in) :: this
     181             :     character(len=*), intent(in) :: k
     182             :     integer :: r
     183             : 
     184             :     integer :: i
     185             : 
     186          19 :     if (this%current_size == 0) then
     187          12 :        r = BUCKET_EMPTY
     188          12 :        return
     189             :     end if
     190             : 
     191           7 :     r = BUCKET_ENTRY_NOT_FOUND
     192           8 :     do i = 1, this%current_size
     193           7 :        if (this%entries(i)%key == trim(k)) then
     194           6 :           r = i
     195           6 :           exit
     196             :        end if
     197             :     end do
     198             : 
     199          26 :   end function find
     200             : 
     201             :   !> Fetch an entry in the dictionary.
     202             :   !!
     203             :   !! \param this the dictionary_t object
     204             :   !! \param k the key
     205             :   !!
     206             :   !! \return the value if found, an empty string else
     207          12 :   function get(this, k) result(r)
     208             :     class(dictionary_t), intent(in) :: this
     209             :     character(len=*), intent(in) :: k
     210             : 
     211             :     character(len=:), allocatable :: r
     212             : 
     213             :     integer :: h, b_idx
     214             : 
     215           6 :     h = this%djb2(k) + 1
     216             : 
     217           6 :     b_idx = this%buckets(h)%find(k)
     218             : 
     219           6 :     if ( (b_idx == BUCKET_EMPTY) .or. &
     220             :          (b_idx == BUCKET_ENTRY_NOT_FOUND) ) then
     221           1 :        r = ''
     222           1 :        return
     223             :     end if
     224             : 
     225           5 :     if (b_idx>0) then
     226           5 :        r = this%buckets(h)%entries(b_idx)%value
     227             :     end if
     228             : 
     229          12 :   end function get
     230             : 
     231           3 : end module dictionary_m

Generated by: LCOV version 1.10