Class | MOFile
In: | lib/gettext/mo.rb
Parent: | Hash

### Methods

hash_string load load_from_file load_from_stream new next_prime open prime? save_to_file save_to_stream set_comment update!

### Classes and Modules

Class MOFile::HeaderRev1
Class MOFile::InvalidFormat

### Constants

Header | = | Struct.new(:magic, :revision, :nstrings, :orig_table_offset, :translated_table_offset, :hash_table_size, :hash_table_offset)
MAGIC_BIG_ENDIAN | = | "\x95\x04\x12\xde"
MAGIC_LITTLE_ENDIAN | = | "\xde\x12\x04\x95"
HASHWORDBITS | = | 32 |  | From gettext-0.12.1/gettext-runtime/intl/hash-string.h Defines the so called `hashpjw’ function by P.J. Weinberger [see Aho/Sethi/Ullman, COMPILERS: Principles, Techniques and Tools, 1986, 1987 Bell Telephone Laboratories, Inc.]

### Attributes

charset | [R] |
filename | [R] |
last_modified | [RW] |
little_endian | [RW] |
nplurals | [R] |
path | [RW] |
plural | [R] |

### Public Class methods

new(output_charset = nil)

[Source]

        # File lib/gettext/mo.rb, line 52
    52:   def initialize(output_charset = nil)
    53:     @filename = nil
    54:     @last_modified = nil
    55:     @little_endian = true
    56:     @output_charset = output_charset
    57:     super()
    58:   end

open(arg = nil, output_charset = nil)

[Source]

        # File lib/gettext/mo.rb, line 47
    47:   def self.open(arg = nil, output_charset = nil)
    48:     result = self.new(output_charset)
    49:     result.load(arg)
    50:   end

### Public Instance methods

hash_string(str)

[Source]

         # File lib/gettext/mo.rb, line 192
    192:   def hash_string(str)
    193:     hval = 0
    194:     i = 0
    195:     str.each_byte do |b|
    196:       break if b == '\0'
    197:       hval <<= 4
    198:       hval += b.to_i
    199:       g = hval & (0xf << (HASHWORDBITS - 4))
    200:       if (g != 0)
    201:         hval ^= g >> (HASHWORDBITS - 8)
    202:         hval ^= g
    203:       end
    204:     end
    205:     hval
    206:   end

load(arg)

[Source]

        # File lib/gettext/mo.rb, line 71
    71:   def load(arg)
    72:     if arg.kind_of? String
    73:       begin
    74:         st = File.stat(arg)
    75:         @last_modified = [st.ctime, st.mtime]
    76:       rescue Exception
    77:       end
    78:       load_from_file(arg)
    79:     else
    80:       load_from_stream(arg)
    81:     end
    82:     @filename = arg
    83:     self
    84:   end

load_from_file(filename)

[Source]

         # File lib/gettext/mo.rb, line 275
    275:   def load_from_file(filename)
    276:     @filename = filename
    277:     begin
    278:       File.open(filename, 'rb'){|f| load_from_stream(f)}
    279:     rescue => e
    280:       e.set_backtrace("File: #{@filename}")
    281:       raise e
    282:     end
    283:   end

load_from_stream(io)

[Source]

         # File lib/gettext/mo.rb, line 86
     86:   def load_from_stream(io)
     87:     magic = io.read(4)
     88:     case magic
     89:     when MAGIC_BIG_ENDIAN
     90:       @little_endian = false
     91:     when MAGIC_LITTLE_ENDIAN
     92:       @little_endian = true
     93:     else
     94:       raise InvalidFormat.new(sprintf("Unknown signature %s", magic.dump))
     95:     end
     96:
     97:     endian_type6 = @little_endian ? 'V6' : 'N6'
     98:     endian_type_astr = @little_endian ? 'V*' : 'N*'
     99:
    100:     header = HeaderRev1.new(magic, *(io.read(4 * 6).unpack(endian_type6)))
    101:
    102:     if header.revision == 1
    103:       # FIXME: It doesn't support sysdep correctly.
    104:       header.n_sysdep_segments = io.read(4).unpack(endian_type6)
    105:       header.sysdep_segments_offset = io.read(4).unpack(endian_type6)
    106:       header.n_sysdep_strings = io.read(4).unpack(endian_type6)
    107:       header.orig_sysdep_tab_offset = io.read(4).unpack(endian_type6)
    108:       header.trans_sysdep_tab_offset = io.read(4).unpack(endian_type6)
    109:     elsif header.revision > 1
    110:       raise InvalidFormat.new(sprintf("file format revision %d isn't supported", header.revision))
    111:     end
    112:     io.pos = header.orig_table_offset
    113:     orig_table_data = io.read((4 * 2) * header.nstrings).unpack(endian_type_astr)
    114:
    115:     io.pos = header.translated_table_offset
    116:     trans_table_data = io.read((4 * 2) * header.nstrings).unpack(endian_type_astr)
    117:
    118:     original_strings = Array.new(header.nstrings)
    119:     for i in 0...header.nstrings
    120:       io.pos = orig_table_data[i * 2 + 1]
    121:       original_strings[i] = io.read(orig_table_data[i * 2 + 0])
    122:     end
    123:
    124:     clear
    125:     for i in 0...header.nstrings
    126:       io.pos = trans_table_data[i * 2 + 1]
    127:       str = io.read(trans_table_data[i * 2 + 0])
    128:
    129:       if (! original_strings[i]) || original_strings[i] == ""
    130:         if str
    131:           @charset = nil
    132:           @nplurals = nil
    133:           @plural = nil
    134:           str.each_line{|line|
    135:             if /^Content-Type:/i =~ line and /charset=((?:\w|-)+)/i =~ line
    136:               @charset = $1
    137:             elsif /^Plural-Forms:\s*nplurals\s*\=\s*(\d*);\s*plural\s*\=\s*([^;]*)\n?/ =~ line
    138:               @nplurals = $1
    139:               @plural = $2
    140:             end
    141:             break if @charset and @nplurals
    142:           }
    143:           @nplurals = "1" unless @nplurals
    144:           @plural = "0" unless @plural
    145:         end
    146:       else
    147:         if @output_charset
    148:           begin
    149:             str = Iconv.conv(@output_charset, @charset, str) if @charset
    150:           rescue Iconv::Failure
    151:             if $DEBUG
    152:               $stderr.print "@charset = ", @charset, "\n"
    153:               $stderr.print "@output_charset = ", @output_charset, "\n"
    154:               $stderr.print "msgid = ", original_strings[i], "\n"
    155:               $stderr.print "msgstr = ", str, "\n"
    156:             end
    157:           end
    158:         end
    159:       end
    160:       self[original_strings[i]] = str.freeze
    161:     end
    162:     self
    163:   end

next_prime(seed)

From gettext-0.12.1/gettext-tools/lib/hash.c

[Source]

         # File lib/gettext/mo.rb, line 179
    179:   def next_prime(seed)
    180:     seed |= 1
    181:     while (! prime?(seed))
    182:       seed += 2
    183:     end
    184:     seed
    185:   end

prime?(candidate)

From gettext-0.12.1/gettext-tools/lib/hash.c

[Source]

         # File lib/gettext/mo.rb, line 166
    166:   def prime?(candidate)
    167:     divn = 3
    168:     sq = divn * divn
    169:
    170:     while (sq < candidate && candidate % divn != 0)
    171:       divn += 1
    172:       sq += 4 * divn
    173:       divn += 1
    174:     end
    175:     candidate % divn != 0
    176:   end

save_to_file(filename)

[Source]

         # File lib/gettext/mo.rb, line 285
    285:   def save_to_file(filename)
    286:     File.open(filename, 'wb'){|f| save_to_stream(f)}
    287:   end

save_to_stream(io)

[Source]

         # File lib/gettext/mo.rb, line 208
    208:   def save_to_stream(io)
    209:     #Save data as little endian format.
    210:     header_size = 4 * 7
    211:     table_size  = 4 * 2 * size
    212:
    213:     hash_table_size = next_prime((size * 4) / 3)
    214:     hash_table_size = 3 if hash_table_size <= 2
    215:     header = Header.new(
    216:       MAGIC_LITTLE_ENDIAN,          # magic
    217:       0,                            # revision
    218:       size,                         # nstrings
    219:       header_size,                  # orig_table_offset
    220:       header_size + table_size,     # translated_table_offset
    221:       hash_table_size,              # hash_table_size
    222:       header_size + table_size * 2  # hash_table_offset
    223:     )
    224:     io.write(header.to_a.pack('a4V*'))
    225:
    226:     ary = to_a
    227:     ary.sort!{|a, b| a[0] <=> b[0]} # sort by original string
    228:
    229:     pos = header.hash_table_size * 4 + header.hash_table_offset
    230:
    231:     orig_table_data = Array.new()
    232:     ary.each{|item, _|
    233:       orig_table_data.push(item.size)
    234:       orig_table_data.push(pos)
    235:       pos += item.size + 1 # +1 is <NUL>
    236:     }
    237:     io.write(orig_table_data.pack('V*'))
    238:
    239:     trans_table_data = Array.new()
    240:     ary.each{|_, item|
    241:       trans_table_data.push(item.size)
    242:       trans_table_data.push(pos)
    243:       pos += item.size + 1 # +1 is <NUL>
    244:     }
    245:     io.write(trans_table_data.pack('V*'))
    246:
    247:     hash_tab = Array.new(hash_table_size)
    248:     j = 0
    249:     ary[0...size].each {|key, _|
    250:       hash_val = hash_string(key)
    251:       idx = hash_val % hash_table_size
    252:       if hash_tab[idx] != nil
    253:         incr = 1 + (hash_val % (hash_table_size - 2))
    254:         begin
    255:           if (idx >= hash_table_size - incr)
    256:             idx -= hash_table_size - incr
    257:           else
    258:             idx += incr
    259:           end
    260:         end until (hash_tab[idx] == nil)
    261:       end
    262:       hash_tab[idx] = j + 1
    263:       j += 1
    264:     }
    265:     hash_tab.collect!{|i| i ? i : 0}
    266:
    267:     io.write(hash_tab.pack('V*'))
    268:
    269:     ary.each{|item, _| io.write(item); io.write("\0") }
    270:     ary.each{|_, item| io.write(item); io.write("\0") }
    271:
    272:     self
    273:   end

set_comment(msgid_or_sym, comment)

[Source]

         # File lib/gettext/mo.rb, line 289
    289:   def set_comment(msgid_or_sym, comment)
    290:     #Do nothing
    291:   end

update!()

[Source]

        # File lib/gettext/mo.rb, line 60
    60:   def update!
    61:     if FileTest.exist?(@filename)
    62:       st = File.stat(@filename)
    63:       load(@filename) unless (@last_modified == [st.ctime, st.mtime])
    64:     else
    65:       puts "#{@filename} was lost." if $DEBUG
    66:       clear
    67:     end
    68:     self
    69:   end

[Validate]