2008-12-28

sprintf/snprintf 陷阱

最近 ubuntu/debian 正在讨论 sprintf/snprintf 的问题,我在这描述一遍。

sprintf 的原型为:
int sprintf(char *restrict s, const char *restrict format, ...);

其中 char* restrict s 的含义为通过s 指向的内存空间不得与其他指针参数指向的内存的空间重叠。比如如下的语句就是错误的用法, 因为参数1与参数3指向的内存重叠了。但这种做法作为增强版的 strcat 已被广泛使用。

sprintf(buf, "%s foo %d %d", buf, var1, var2);

在 ubuntu 8.10 所带的 gcc 中,如果编译时加入了优化选项(比如 -O1, -O2), 那么sprinf 会首先将 s 清空,比如如下的程序会输出 "fail", 而不是 "not fail"。

#include
char buf[80] = "not ";
int main()
{
sprintf(buf, "%sfail", buf);
puts(buf);
return 0;
}






作为补救方案,可以使用如下的语句来代替:

sprintf(buf+strlen(buf), " foo %d %d", var1, var2);

snprintf 有类似的 bug.

很多 Debian/Ubuntu 包都有此 bug,可以参见如下两个 thread 中的具体内容

[1] https://bugs.launchpad.net/ubuntu/+source/glibc/+bug/305901
[2] http://groups.google.com/group/linux.debian.devel/browse_thread/thread/c5bbda39f1a01bd4

2008-12-14



1. 通过数据层访问数据库


最简单的模型,采用一个数据访问层,来隔离应用与数据库。
public class MyDA {
//...
}

public class MyApp {
public MyApp() {
da = new MyDA();
}
private MyDA da;
//...
}

2. 面向界面编程,同时方便使用 Mock 方法测试

public interface IMyDA {
//...
}


public class MyDA implements IMyDA {

//...
}

public class MyApp {
public MyApp() {
setDA(new MyDA());
}
protected void setDA(IMyDA da) {
this.da = da;
}
private IMyDA da;
//...
}



关于 Mock 测试,参见 http://www.ibm.com/developerworks/library/j-mocktest.html

3. 引入工厂模式,隔离数据访问与业务逻辑




public interface IMyDA {
//...
}

public class MyDA implements IMyDA {
//...
}

public class MyDAFactory {
public IMyDA createMyDA(String name) {
if(name.equals("foo")) {
return new MyDA();
} else {
throw new Exception();
}
}
}


public class MyApp {
public MyApp() {
setDA(MyDAFactory.createMyDA("foo"));
}
protected void setDA(IMyDA da) {
this.da = da;
}
private IMyDA da;
//...
}



这种情况下 MyApp 仍然通过 MyDAFactory 来依赖与 MyDA, 甚至各种 IMyDA 的实现。另外,工厂模式不方便添加新的实现。



4. 引入 IoC 容器


Java 源码部分



public interface IMyDA {
//...
}

public class MyDA implements IMyDA {
//...
}


public class MyApp {
public MyApp() {
BeanFactory factory = new XmlBeanFactory(
new FileInputStream("myapp.xml"));

da = (IMyDA) factory.getBean("da");
}

private IMyDA da;

//...
}

用到的 myapp.xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="da" class="MyDA"/>
</beans>

5. 一个复杂点的例子


上面的例子有点小题大做了,在真实的环境中,数据访问一般需要通过 MySQL库, 连接池, cache, 甚至更多的层,每一层都需要可以配置,最好也支持替换(方便测试),设置还需要支持删除某些中间层,工厂模式用于这样的情形则非常麻烦,但是 IoC 处理这样的情形则比较简单,比如把如下的文件替换掉上面的myapp.xml,那么你的应用的数据访问层就变成了用于数据库连接池和cache功能的 SuperMyDA。



更重要的是,隔离非常彻底,在你编写编译 MyApp 的时候,你根本就用不着 proxool, memcache 这类组件。



注意: 下面文件里面的内容都是乱写的,我还没去查这些组件的接口, :-)



<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">

<!-- 配置 mysql 连接 -->
<bean id="db" class="com.mysql.Connection">
<property name="uri"
value="mysql://userName:secret@192.168.12.23/dbname"/>
</bean>

<!-- 配置 proxool -->
<bean id="proxool" class="com.proxool.Pool">
<property name="db" ref="db"/>
<property name="maxConnection" value="200"/>
</bean>

<!-- 配置 memcache -->
<bean id="memcache" class="com.memcache.Foo">
<property name="config" value="192.168.23.34:12345">
</bean>

<!-- 配置 SuperMyDA -->
<bean id="da" class="SuperMyDA">
<property name="proxool" ref="proxool"/>
<property name="memcache" ref="memcache"/>
</bean>

</beans>

2008-10-19

sound card support for Dell Inspiron 1420 under Debian



$ sudo udevadm trigger