...
Run Format

Source file src/runtime/iface.go

Documentation: runtime

     1  // Copyright 2014 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package runtime
     6  
     7  import (
     8  	"runtime/internal/atomic"
     9  	"runtime/internal/sys"
    10  	"unsafe"
    11  )
    12  
    13  const itabInitSize = 512
    14  
    15  var (
    16  	itabLock      mutex                               // lock for accessing itab table
    17  	itabTable     = &itabTableInit                    // pointer to current table
    18  	itabTableInit = itabTableType{size: itabInitSize} // starter table
    19  )
    20  
    21  // Note: change the formula in the mallocgc call in itabAdd if you change these fields.
    22  type itabTableType struct {
    23  	size    uintptr             // length of entries array. Always a power of 2.
    24  	count   uintptr             // current number of filled entries.
    25  	entries [itabInitSize]*itab // really [size] large
    26  }
    27  
    28  func itabHashFunc(inter *interfacetype, typ *_type) uintptr {
    29  	// compiler has provided some good hash codes for us.
    30  	return uintptr(inter.typ.hash ^ typ.hash)
    31  }
    32  
    33  func getitab(inter *interfacetype, typ *_type, canfail bool) *itab {
    34  	if len(inter.mhdr) == 0 {
    35  		throw("internal error - misuse of itab")
    36  	}
    37  
    38  	// easy case
    39  	if typ.tflag&tflagUncommon == 0 {
    40  		if canfail {
    41  			return nil
    42  		}
    43  		name := inter.typ.nameOff(inter.mhdr[0].name)
    44  		panic(&TypeAssertionError{nil, typ, &inter.typ, name.name()})
    45  	}
    46  
    47  	var m *itab
    48  
    49  	// First, look in the existing table to see if we can find the itab we need.
    50  	// This is by far the most common case, so do it without locks.
    51  	// Use atomic to ensure we see any previous writes done by the thread
    52  	// that updates the itabTable field (with atomic.Storep in itabAdd).
    53  	t := (*itabTableType)(atomic.Loadp(unsafe.Pointer(&itabTable)))
    54  	if m = t.find(inter, typ); m != nil {
    55  		goto finish
    56  	}
    57  
    58  	// Not found.  Grab the lock and try again.
    59  	lock(&itabLock)
    60  	if m = itabTable.find(inter, typ); m != nil {
    61  		unlock(&itabLock)
    62  		goto finish
    63  	}
    64  
    65  	// Entry doesn't exist yet. Make a new entry & add it.
    66  	m = (*itab)(persistentalloc(unsafe.Sizeof(itab{})+uintptr(len(inter.mhdr)-1)*sys.PtrSize, 0, &memstats.other_sys))
    67  	m.inter = inter
    68  	m._type = typ
    69  	m.init()
    70  	itabAdd(m)
    71  	unlock(&itabLock)
    72  finish:
    73  	if m.fun[0] != 0 {
    74  		return m
    75  	}
    76  	if canfail {
    77  		return nil
    78  	}
    79  	// this can only happen if the conversion
    80  	// was already done once using the , ok form
    81  	// and we have a cached negative result.
    82  	// The cached result doesn't record which
    83  	// interface function was missing, so initialize
    84  	// the itab again to get the missing function name.
    85  	panic(&TypeAssertionError{concrete: typ, asserted: &inter.typ, missingMethod: m.init()})
    86  }
    87  
    88  // find finds the given interface/type pair in t.
    89  // Returns nil if the given interface/type pair isn't present.
    90  func (t *itabTableType) find(inter *interfacetype, typ *_type) *itab {
    91  	// Implemented using quadratic probing.
    92  	// Probe sequence is h(i) = h0 + i*(i+1)/2 mod 2^k.
    93  	// We're guaranteed to hit all table entries using this probe sequence.
    94  	mask := t.size - 1
    95  	h := itabHashFunc(inter, typ) & mask
    96  	for i := uintptr(1); ; i++ {
    97  		p := (**itab)(add(unsafe.Pointer(&t.entries), h*sys.PtrSize))
    98  		// Use atomic read here so if we see m != nil, we also see
    99  		// the initializations of the fields of m.
   100  		// m := *p
   101  		m := (*itab)(atomic.Loadp(unsafe.Pointer(p)))
   102  		if m == nil {
   103  			return nil
   104  		}
   105  		if m.inter == inter && m._type == typ {
   106  			return m
   107  		}
   108  		h += i
   109  		h &= mask
   110  	}
   111  }
   112  
   113  // itabAdd adds the given itab to the itab hash table.
   114  // itabLock must be held.
   115  func itabAdd(m *itab) {
   116  	// Bugs can lead to calling this while mallocing is set,
   117  	// typically because this is called while panicing.
   118  	// Crash reliably, rather than only when we need to grow
   119  	// the hash table.
   120  	if getg().m.mallocing != 0 {
   121  		throw("malloc deadlock")
   122  	}
   123  
   124  	t := itabTable
   125  	if t.count >= 3*(t.size/4) { // 75% load factor
   126  		// Grow hash table.
   127  		// t2 = new(itabTableType) + some additional entries
   128  		// We lie and tell malloc we want pointer-free memory because
   129  		// all the pointed-to values are not in the heap.
   130  		t2 := (*itabTableType)(mallocgc((2+2*t.size)*sys.PtrSize, nil, true))
   131  		t2.size = t.size * 2
   132  
   133  		// Copy over entries.
   134  		// Note: while copying, other threads may look for an itab and
   135  		// fail to find it. That's ok, they will then try to get the itab lock
   136  		// and as a consequence wait until this copying is complete.
   137  		iterate_itabs(t2.add)
   138  		if t2.count != t.count {
   139  			throw("mismatched count during itab table copy")
   140  		}
   141  		// Publish new hash table. Use an atomic write: see comment in getitab.
   142  		atomicstorep(unsafe.Pointer(&itabTable), unsafe.Pointer(t2))
   143  		// Adopt the new table as our own.
   144  		t = itabTable
   145  		// Note: the old table can be GC'ed here.
   146  	}
   147  	t.add(m)
   148  }
   149  
   150  // add adds the given itab to itab table t.
   151  // itabLock must be held.
   152  func (t *itabTableType) add(m *itab) {
   153  	// See comment in find about the probe sequence.
   154  	// Insert new itab in the first empty spot in the probe sequence.
   155  	mask := t.size - 1
   156  	h := itabHashFunc(m.inter, m._type) & mask
   157  	for i := uintptr(1); ; i++ {
   158  		p := (**itab)(add(unsafe.Pointer(&t.entries), h*sys.PtrSize))
   159  		m2 := *p
   160  		if m2 == m {
   161  			// A given itab may be used in more than one module
   162  			// and thanks to the way global symbol resolution works, the
   163  			// pointed-to itab may already have been inserted into the
   164  			// global 'hash'.
   165  			return
   166  		}
   167  		if m2 == nil {
   168  			// Use atomic write here so if a reader sees m, it also
   169  			// sees the correctly initialized fields of m.
   170  			// NoWB is ok because m is not in heap memory.
   171  			// *p = m
   172  			atomic.StorepNoWB(unsafe.Pointer(p), unsafe.Pointer(m))
   173  			t.count++
   174  			return
   175  		}
   176  		h += i
   177  		h &= mask
   178  	}
   179  }
   180  
   181  // init fills in the m.fun array with all the code pointers for
   182  // the m.inter/m._type pair. If the type does not implement the interface,
   183  // it sets m.fun[0] to 0 and returns the name of an interface function that is missing.
   184  // It is ok to call this multiple times on the same m, even concurrently.
   185  func (m *itab) init() string {
   186  	inter := m.inter
   187  	typ := m._type
   188  	x := typ.uncommon()
   189  
   190  	// both inter and typ have method sorted by name,
   191  	// and interface names are unique,
   192  	// so can iterate over both in lock step;
   193  	// the loop is O(ni+nt) not O(ni*nt).
   194  	ni := len(inter.mhdr)
   195  	nt := int(x.mcount)
   196  	xmhdr := (*[1 << 16]method)(add(unsafe.Pointer(x), uintptr(x.moff)))[:nt:nt]
   197  	j := 0
   198  imethods:
   199  	for k := 0; k < ni; k++ {
   200  		i := &inter.mhdr[k]
   201  		itype := inter.typ.typeOff(i.ityp)
   202  		name := inter.typ.nameOff(i.name)
   203  		iname := name.name()
   204  		ipkg := name.pkgPath()
   205  		if ipkg == "" {
   206  			ipkg = inter.pkgpath.name()
   207  		}
   208  		for ; j < nt; j++ {
   209  			t := &xmhdr[j]
   210  			tname := typ.nameOff(t.name)
   211  			if typ.typeOff(t.mtyp) == itype && tname.name() == iname {
   212  				pkgPath := tname.pkgPath()
   213  				if pkgPath == "" {
   214  					pkgPath = typ.nameOff(x.pkgpath).name()
   215  				}
   216  				if tname.isExported() || pkgPath == ipkg {
   217  					if m != nil {
   218  						ifn := typ.textOff(t.ifn)
   219  						*(*unsafe.Pointer)(add(unsafe.Pointer(&m.fun[0]), uintptr(k)*sys.PtrSize)) = ifn
   220  					}
   221  					continue imethods
   222  				}
   223  			}
   224  		}
   225  		// didn't find method
   226  		m.fun[0] = 0
   227  		return iname
   228  	}
   229  	m.hash = typ.hash
   230  	return ""
   231  }
   232  
   233  func itabsinit() {
   234  	lock(&itabLock)
   235  	for _, md := range activeModules() {
   236  		for _, i := range md.itablinks {
   237  			itabAdd(i)
   238  		}
   239  	}
   240  	unlock(&itabLock)
   241  }
   242  
   243  // panicdottypeE is called when doing an e.(T) conversion and the conversion fails.
   244  // have = the dynamic type we have.
   245  // want = the static type we're trying to convert to.
   246  // iface = the static type we're converting from.
   247  func panicdottypeE(have, want, iface *_type) {
   248  	panic(&TypeAssertionError{iface, have, want, ""})
   249  }
   250  
   251  // panicdottypeI is called when doing an i.(T) conversion and the conversion fails.
   252  // Same args as panicdottypeE, but "have" is the dynamic itab we have.
   253  func panicdottypeI(have *itab, want, iface *_type) {
   254  	var t *_type
   255  	if have != nil {
   256  		t = have._type
   257  	}
   258  	panicdottypeE(t, want, iface)
   259  }
   260  
   261  // panicnildottype is called when doing a i.(T) conversion and the interface i is nil.
   262  // want = the static type we're trying to convert to.
   263  func panicnildottype(want *_type) {
   264  	panic(&TypeAssertionError{nil, nil, want, ""})
   265  	// TODO: Add the static type we're converting from as well.
   266  	// It might generate a better error message.
   267  	// Just to match other nil conversion errors, we don't for now.
   268  }
   269  
   270  // The conv and assert functions below do very similar things.
   271  // The convXXX functions are guaranteed by the compiler to succeed.
   272  // The assertXXX functions may fail (either panicking or returning false,
   273  // depending on whether they are 1-result or 2-result).
   274  // The convXXX functions succeed on a nil input, whereas the assertXXX
   275  // functions fail on a nil input.
   276  
   277  func convT2E(t *_type, elem unsafe.Pointer) (e eface) {
   278  	if raceenabled {
   279  		raceReadObjectPC(t, elem, getcallerpc(), funcPC(convT2E))
   280  	}
   281  	if msanenabled {
   282  		msanread(elem, t.size)
   283  	}
   284  	x := mallocgc(t.size, t, true)
   285  	// TODO: We allocate a zeroed object only to overwrite it with actual data.
   286  	// Figure out how to avoid zeroing. Also below in convT2Eslice, convT2I, convT2Islice.
   287  	typedmemmove(t, x, elem)
   288  	e._type = t
   289  	e.data = x
   290  	return
   291  }
   292  
   293  func convT2E16(t *_type, val uint16) (e eface) {
   294  	var x unsafe.Pointer
   295  	if val == 0 {
   296  		x = unsafe.Pointer(&zeroVal[0])
   297  	} else {
   298  		x = mallocgc(2, t, false)
   299  		*(*uint16)(x) = val
   300  	}
   301  	e._type = t
   302  	e.data = x
   303  	return
   304  }
   305  
   306  func convT2E32(t *_type, val uint32) (e eface) {
   307  	var x unsafe.Pointer
   308  	if val == 0 {
   309  		x = unsafe.Pointer(&zeroVal[0])
   310  	} else {
   311  		x = mallocgc(4, t, false)
   312  		*(*uint32)(x) = val
   313  	}
   314  	e._type = t
   315  	e.data = x
   316  	return
   317  }
   318  
   319  func convT2E64(t *_type, val uint64) (e eface) {
   320  	var x unsafe.Pointer
   321  	if val == 0 {
   322  		x = unsafe.Pointer(&zeroVal[0])
   323  	} else {
   324  		x = mallocgc(8, t, false)
   325  		*(*uint64)(x) = val
   326  	}
   327  	e._type = t
   328  	e.data = x
   329  	return
   330  }
   331  
   332  func convT2Estring(t *_type, elem unsafe.Pointer) (e eface) {
   333  	if raceenabled {
   334  		raceReadObjectPC(t, elem, getcallerpc(), funcPC(convT2Estring))
   335  	}
   336  	if msanenabled {
   337  		msanread(elem, t.size)
   338  	}
   339  	var x unsafe.Pointer
   340  	if *(*string)(elem) == "" {
   341  		x = unsafe.Pointer(&zeroVal[0])
   342  	} else {
   343  		x = mallocgc(t.size, t, true)
   344  		*(*string)(x) = *(*string)(elem)
   345  	}
   346  	e._type = t
   347  	e.data = x
   348  	return
   349  }
   350  
   351  func convT2Eslice(t *_type, elem unsafe.Pointer) (e eface) {
   352  	if raceenabled {
   353  		raceReadObjectPC(t, elem, getcallerpc(), funcPC(convT2Eslice))
   354  	}
   355  	if msanenabled {
   356  		msanread(elem, t.size)
   357  	}
   358  	var x unsafe.Pointer
   359  	if v := *(*slice)(elem); uintptr(v.array) == 0 {
   360  		x = unsafe.Pointer(&zeroVal[0])
   361  	} else {
   362  		x = mallocgc(t.size, t, true)
   363  		*(*slice)(x) = *(*slice)(elem)
   364  	}
   365  	e._type = t
   366  	e.data = x
   367  	return
   368  }
   369  
   370  func convT2Enoptr(t *_type, elem unsafe.Pointer) (e eface) {
   371  	if raceenabled {
   372  		raceReadObjectPC(t, elem, getcallerpc(), funcPC(convT2Enoptr))
   373  	}
   374  	if msanenabled {
   375  		msanread(elem, t.size)
   376  	}
   377  	x := mallocgc(t.size, t, false)
   378  	memmove(x, elem, t.size)
   379  	e._type = t
   380  	e.data = x
   381  	return
   382  }
   383  
   384  func convT2I(tab *itab, elem unsafe.Pointer) (i iface) {
   385  	t := tab._type
   386  	if raceenabled {
   387  		raceReadObjectPC(t, elem, getcallerpc(), funcPC(convT2I))
   388  	}
   389  	if msanenabled {
   390  		msanread(elem, t.size)
   391  	}
   392  	x := mallocgc(t.size, t, true)
   393  	typedmemmove(t, x, elem)
   394  	i.tab = tab
   395  	i.data = x
   396  	return
   397  }
   398  
   399  func convT2I16(tab *itab, val uint16) (i iface) {
   400  	t := tab._type
   401  	var x unsafe.Pointer
   402  	if val == 0 {
   403  		x = unsafe.Pointer(&zeroVal[0])
   404  	} else {
   405  		x = mallocgc(2, t, false)
   406  		*(*uint16)(x) = val
   407  	}
   408  	i.tab = tab
   409  	i.data = x
   410  	return
   411  }
   412  
   413  func convT2I32(tab *itab, val uint32) (i iface) {
   414  	t := tab._type
   415  	var x unsafe.Pointer
   416  	if val == 0 {
   417  		x = unsafe.Pointer(&zeroVal[0])
   418  	} else {
   419  		x = mallocgc(4, t, false)
   420  		*(*uint32)(x) = val
   421  	}
   422  	i.tab = tab
   423  	i.data = x
   424  	return
   425  }
   426  
   427  func convT2I64(tab *itab, val uint64) (i iface) {
   428  	t := tab._type
   429  	var x unsafe.Pointer
   430  	if val == 0 {
   431  		x = unsafe.Pointer(&zeroVal[0])
   432  	} else {
   433  		x = mallocgc(8, t, false)
   434  		*(*uint64)(x) = val
   435  	}
   436  	i.tab = tab
   437  	i.data = x
   438  	return
   439  }
   440  
   441  func convT2Istring(tab *itab, elem unsafe.Pointer) (i iface) {
   442  	t := tab._type
   443  	if raceenabled {
   444  		raceReadObjectPC(t, elem, getcallerpc(), funcPC(convT2Istring))
   445  	}
   446  	if msanenabled {
   447  		msanread(elem, t.size)
   448  	}
   449  	var x unsafe.Pointer
   450  	if *(*string)(elem) == "" {
   451  		x = unsafe.Pointer(&zeroVal[0])
   452  	} else {
   453  		x = mallocgc(t.size, t, true)
   454  		*(*string)(x) = *(*string)(elem)
   455  	}
   456  	i.tab = tab
   457  	i.data = x
   458  	return
   459  }
   460  
   461  func convT2Islice(tab *itab, elem unsafe.Pointer) (i iface) {
   462  	t := tab._type
   463  	if raceenabled {
   464  		raceReadObjectPC(t, elem, getcallerpc(), funcPC(convT2Islice))
   465  	}
   466  	if msanenabled {
   467  		msanread(elem, t.size)
   468  	}
   469  	var x unsafe.Pointer
   470  	if v := *(*slice)(elem); uintptr(v.array) == 0 {
   471  		x = unsafe.Pointer(&zeroVal[0])
   472  	} else {
   473  		x = mallocgc(t.size, t, true)
   474  		*(*slice)(x) = *(*slice)(elem)
   475  	}
   476  	i.tab = tab
   477  	i.data = x
   478  	return
   479  }
   480  
   481  func convT2Inoptr(tab *itab, elem unsafe.Pointer) (i iface) {
   482  	t := tab._type
   483  	if raceenabled {
   484  		raceReadObjectPC(t, elem, getcallerpc(), funcPC(convT2Inoptr))
   485  	}
   486  	if msanenabled {
   487  		msanread(elem, t.size)
   488  	}
   489  	x := mallocgc(t.size, t, false)
   490  	memmove(x, elem, t.size)
   491  	i.tab = tab
   492  	i.data = x
   493  	return
   494  }
   495  
   496  func convI2I(inter *interfacetype, i iface) (r iface) {
   497  	tab := i.tab
   498  	if tab == nil {
   499  		return
   500  	}
   501  	if tab.inter == inter {
   502  		r.tab = tab
   503  		r.data = i.data
   504  		return
   505  	}
   506  	r.tab = getitab(inter, tab._type, false)
   507  	r.data = i.data
   508  	return
   509  }
   510  
   511  func assertI2I(inter *interfacetype, i iface) (r iface) {
   512  	tab := i.tab
   513  	if tab == nil {
   514  		// explicit conversions require non-nil interface value.
   515  		panic(&TypeAssertionError{nil, nil, &inter.typ, ""})
   516  	}
   517  	if tab.inter == inter {
   518  		r.tab = tab
   519  		r.data = i.data
   520  		return
   521  	}
   522  	r.tab = getitab(inter, tab._type, false)
   523  	r.data = i.data
   524  	return
   525  }
   526  
   527  func assertI2I2(inter *interfacetype, i iface) (r iface, b bool) {
   528  	tab := i.tab
   529  	if tab == nil {
   530  		return
   531  	}
   532  	if tab.inter != inter {
   533  		tab = getitab(inter, tab._type, true)
   534  		if tab == nil {
   535  			return
   536  		}
   537  	}
   538  	r.tab = tab
   539  	r.data = i.data
   540  	b = true
   541  	return
   542  }
   543  
   544  func assertE2I(inter *interfacetype, e eface) (r iface) {
   545  	t := e._type
   546  	if t == nil {
   547  		// explicit conversions require non-nil interface value.
   548  		panic(&TypeAssertionError{nil, nil, &inter.typ, ""})
   549  	}
   550  	r.tab = getitab(inter, t, false)
   551  	r.data = e.data
   552  	return
   553  }
   554  
   555  func assertE2I2(inter *interfacetype, e eface) (r iface, b bool) {
   556  	t := e._type
   557  	if t == nil {
   558  		return
   559  	}
   560  	tab := getitab(inter, t, true)
   561  	if tab == nil {
   562  		return
   563  	}
   564  	r.tab = tab
   565  	r.data = e.data
   566  	b = true
   567  	return
   568  }
   569  
   570  //go:linkname reflect_ifaceE2I reflect.ifaceE2I
   571  func reflect_ifaceE2I(inter *interfacetype, e eface, dst *iface) {
   572  	*dst = assertE2I(inter, e)
   573  }
   574  
   575  func iterate_itabs(fn func(*itab)) {
   576  	// Note: only runs during stop the world or with itabLock held,
   577  	// so no other locks/atomics needed.
   578  	t := itabTable
   579  	for i := uintptr(0); i < t.size; i++ {
   580  		m := *(**itab)(add(unsafe.Pointer(&t.entries), i*sys.PtrSize))
   581  		if m != nil {
   582  			fn(m)
   583  		}
   584  	}
   585  }
   586  
   587  // staticbytes is used to avoid convT2E for byte-sized values.
   588  var staticbytes = [...]byte{
   589  	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
   590  	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
   591  	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
   592  	0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
   593  	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
   594  	0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
   595  	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
   596  	0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
   597  	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
   598  	0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
   599  	0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
   600  	0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
   601  	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
   602  	0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
   603  	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
   604  	0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
   605  	0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
   606  	0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
   607  	0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
   608  	0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
   609  	0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
   610  	0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
   611  	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
   612  	0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
   613  	0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
   614  	0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
   615  	0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
   616  	0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
   617  	0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
   618  	0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
   619  	0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
   620  	0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
   621  }
   622  

View as plain text